diff --git a/catalog/C10_Catalog.py b/catalog/C10_Catalog.py new file mode 100644 index 0000000000000000000000000000000000000000..3168b1746c98e70213f0eb61fa44d36d1a5d2aa6 --- /dev/null +++ b/catalog/C10_Catalog.py @@ -0,0 +1,578 @@ +import os +import galsim +import random +import copy +import numpy as np +import h5py as h5 +import healpy as hp +import astropy.constants as cons +import traceback +from astropy.coordinates import spherical_to_cartesian +from astropy.table import Table +from scipy import interpolate +from datetime import datetime + +from observation_sim.mock_objects import CatalogBase, Star, Galaxy, Quasar, ExtinctionMW +from observation_sim.mock_objects._util import tag_sed, getObservedSED, getABMAG, integrate_sed_bandpass, comoving_dist +from observation_sim.astrometry.Astrometry_util import on_orbit_obs_position + +# (TEST) +from astropy.cosmology import FlatLambdaCDM +from astropy import constants +from astropy import units as U +from astropy.coordinates import SkyCoord +from astropy.io import fits +import astropy.constants as atcons + +import ctypes + +try: + import importlib.resources as pkg_resources +except ImportError: + # Try backported to PY<37 'importlib_resources' + import importlib_resources as pkg_resources + +NSIDE = 128 + +pointing_file_list = ['pointing_C10_1000deg_lat80.dat', 'pointing_C10_1000deg_latm80_.dat', 'pointing_C10_1000deg_p180_m30.dat', 'pointing_C10_1000deg_p320_m40.dat'] +star_file_list = ['CSST_C10_1000sqd_1.hdf5', 'CSST_C10_1000sqd_2_Fornax.hdf5', 'CSST_C10_1000sqd_3.hdf5', 'CSST_C10_1000sqd_4.hdf5'] + + +def get_galaxy_qso_list(config): + cat_dir = config["catalog_options"]["input_path"]["cat_dir"] + with open(cat_dir+"galcat_C10/qsocat/gal_C10_file", 'r', encoding='utf-8') as fn1: + fn1_list = fn1.readlines() + bundle_file_list = [line.strip() for line in fn1_list] + with open(cat_dir+"galcat_C10/qsocat/qso_sed_C10_file", 'r', encoding='utf-8') as fn2: + fn2_list = fn2.readlines() + qsosed_file_list = [line.strip() for line in fn2_list] + return bundle_file_list, qsosed_file_list + + +class StarParm(ctypes.Structure): + _fields_ = [ + ('logte', ctypes.c_float), + ('logg', ctypes.c_float), + ('Mass', ctypes.c_float), + ('Av', ctypes.c_float), + ('mu0', ctypes.c_float), + ('Z', ctypes.c_float)] + + +def get_star_cat(config): + idx = pointing_file_list.index(os.path.basename(config['obs_setting']['pointing_file'])) + return_star_path = star_file_list[idx] + return return_star_path + + +def get_bundleIndex(healpixID_ring, bundleOrder=4, healpixOrder=7): + assert NSIDE == 2**healpixOrder + shift = healpixOrder - bundleOrder + shift = 2*shift + + nside_bundle = 2**bundleOrder + nside_healpix = 2**healpixOrder + + healpixID_nest = hp.ring2nest(nside_healpix, healpixID_ring) + bundleID_nest = (healpixID_nest >> shift) + bundleID_ring = hp.nest2ring(nside_bundle, bundleID_nest) + + return bundleID_ring + + +def get_agnsed_file(bundle_file_name, config): + bundle_file_list, qsosed_file_list = get_galaxy_qso_list(config) + return qsosed_file_list[bundle_file_list.index(bundle_file_name)] + + +class Catalog(CatalogBase): + def __init__(self, config, chip, pointing, chip_output, filt, **kwargs): + super().__init__() + self.cat_dir = config["catalog_options"]["input_path"]["cat_dir"] + + self.cosmo = FlatLambdaCDM(H0=67.66, Om0=0.3111) + + self.chip_output = chip_output + self.filt = filt + self.logger = chip_output.logger + + with pkg_resources.path('catalog.data', 'SLOAN_SDSS.g.fits') as filter_path: + self.normF_star = Table.read(str(filter_path)) + + self.config = config + self.chip = chip + self.pointing = pointing + + self.max_size = 0. + + # [TODO] Milky Way extinction + if "enable_mw_ext_gal" in config["catalog_options"] and config["catalog_options"]["enable_mw_ext_gal"]: + if "planck_ebv_map" not in config["catalog_options"]: + raise ValueError( + "Planck dust map must be given to enable Milky Way extinction calculation for galaxies.") + self.mw_ext = ExtinctionMW() + self.mw_ext.init_ext_model(model_name="odonnell") + self.mw_ext.load_Planck_ext( + file_path=config["catalog_options"]["planck_ebv_map"]) + + if "star_cat" in config["catalog_options"]["input_path"] and config["catalog_options"]["input_path"]["star_cat"] and not config["catalog_options"]["galaxy_only"]: + # Get the cloest star catalog file + star_file_name = get_star_cat(self.config) + star_path = os.path.join(config["catalog_options"]["input_path"]["star_cat"], star_file_name) + self.star_path = os.path.join(self.cat_dir, star_path) + self.star_SED_path = config["catalog_options"]["SED_templates_path"]["star_SED"] + self._load_SED_lib_star() + + if "galaxy_cat" in config["catalog_options"]["input_path"] and config["catalog_options"]["input_path"]["galaxy_cat"] and not config["catalog_options"]["star_only"]: + galaxy_dir = config["catalog_options"]["input_path"]["galaxy_cat"] + self.galaxy_path = os.path.join(self.cat_dir, galaxy_dir) + self.galaxy_SED_path = config["catalog_options"]["SED_templates_path"]["galaxy_SED"] + self._load_SED_lib_gals() + self.agn_seds = {} + + if "AGN_SED" in config["catalog_options"]["SED_templates_path"] and not config["catalog_options"]["star_only"]: + self.AGN_SED_path = config["catalog_options"]["SED_templates_path"]["AGN_SED"] + + if "rotateEll" in config["catalog_options"]: + self.rotation = np.radians( + float(config["catalog_options"]["rotateEll"])) + else: + self.rotation = 0. + + # Update output .cat header with catalog specific output columns + self._add_output_columns_header() + + self._get_healpix_list() + self._load() + + def _add_output_columns_header(self): + self.add_hdr = " av stellarmass dm teff logg feh" + self.add_hdr += " bulgemass diskmass detA e1 e2 kappa g1 g2 size galType veldisp " + + self.add_fmt = "%8.4f %8.4f %8.4f %8.4f %8.4f %8.4f" + self.add_fmt += " %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %4d %8.4f " + self.chip_output.update_output_header( + additional_column_names=self.add_hdr) + + def _get_healpix_list(self): + self.sky_coverage = self.chip.getSkyCoverageEnlarged( + self.chip.img.wcs, margin=0.2) + ra_min, ra_max, dec_min, dec_max = self.sky_coverage.xmin, self.sky_coverage.xmax, self.sky_coverage.ymin, self.sky_coverage.ymax + ra = np.deg2rad(np.array([ra_min, ra_max, ra_max, ra_min])) + dec = np.deg2rad(np.array([dec_max, dec_max, dec_min, dec_min])) + self.pix_list = hp.query_polygon( + NSIDE, + hp.ang2vec(np.radians(90.) - dec, ra), + inclusive=True + ) + if self.logger is not None: + msg = str(("HEALPix List: ", self.pix_list)) + self.logger.info(msg) + else: + print("HEALPix List: ", self.pix_list) + + def load_norm_filt(self, obj): + if obj.type == "star": + return self.normF_star + elif obj.type == "galaxy" or obj.type == "quasar": + # return self.normF_galaxy + return None + else: + return None + + # def _load_SED_lib_star(self): + # self.tempSED_star = h5.File(self.star_SED_path,'r') + def _load_SED_lib_star(self): + # self.tempSED_star = h5.File(self.star_SED_path,'r') + with pkg_resources.path('catalog.data', 'starSpecInterp.so') as ddl_path: + self.starDDL = ctypes.CDLL(str(ddl_path)) + self.starDDL.loadSpecLibs.argtypes = [ctypes.c_char_p, ctypes.c_char_p] + self.starDDL.loadExts.argtypes = [ctypes.c_char_p] + nwv = self.starDDL.loadSpecLibs(str.encode(os.path.join( + self.star_SED_path, 'file_BT-Settl_CSST_wl1000-24000_R1000.par')), str.encode(self.star_SED_path)) + self.starDDL.loadExts(str.encode(os.path.join( + self.star_SED_path, "Ext_odonnell94_R3.1_CSST_wl1000-24000_R1000.fits"))) + self.star_spec_len = nwv + + def _interp_star_sed(self, obj): + spec = (ctypes.c_float*self.star_spec_len)() + wave = (ctypes.c_float*self.star_spec_len)() + self.starDDL.interpSingleStar.argtypes = [ + ctypes.Structure, ctypes.POINTER(ctypes.c_float)] + # s=Star(obj.param['teff'], obj.param['grav''], obj.paramstar['mwmsc_mass'], obj.param['AV'], obj.param['DM'], obj.param['z_met']) + s = StarParm(obj.param['teff'], obj.param['logg'], obj.param['stellarMass'], + obj.param['av'], obj.param['DM'], obj.param['feh']) + + self.starDDL.interpSingleStar(s, spec, wave) + + rv_c = obj.param['rv']/(atcons.c.value/1000.) + Doppler_factor = np.sqrt((1+rv_c)/(1-rv_c)) + wave_RV = wave*Doppler_factor + return wave_RV, np.power(10, spec[:]) + + def _load_SED_lib_gals(self): + pcs = h5.File(os.path.join(self.galaxy_SED_path, "pcs.h5"), "r") + lamb = h5.File(os.path.join(self.galaxy_SED_path, "lamb.h5"), "r") + self.lamb_gal = lamb['lamb'][()] + self.pcs = pcs['pcs'][()] + + def _load_gals(self, gals, pix_id=None, cat_id=0, agnsed_file=""): + ngals = len(gals['ra']) + + # Apply astrometric modeling + ra_arr = gals['ra'][:] + dec_arr = gals['dec'][:] + if self.config["obs_setting"]["enable_astrometric_model"]: + ra_list = ra_arr.tolist() + dec_list = dec_arr.tolist() + pmra_list = np.zeros(ngals).tolist() + pmdec_list = np.zeros(ngals).tolist() + rv_list = np.zeros(ngals).tolist() + parallax_list = [1e-9] * ngals + dt = datetime.utcfromtimestamp(self.pointing.timestamp) + date_str = dt.date().isoformat() + time_str = dt.time().isoformat() + ra_arr, dec_arr = on_orbit_obs_position( + input_ra_list=ra_list, + input_dec_list=dec_list, + input_pmra_list=pmra_list, + input_pmdec_list=pmdec_list, + input_rv_list=rv_list, + input_parallax_list=parallax_list, + input_nstars=ngals, + input_x=self.pointing.sat_x, + input_y=self.pointing.sat_y, + input_z=self.pointing.sat_z, + input_vx=self.pointing.sat_vx, + input_vy=self.pointing.sat_vy, + input_vz=self.pointing.sat_vz, + input_epoch="J2000", + input_date_str=date_str, + input_time_str=time_str + ) + + # [TODO] get Milky Way extinction AVs + if "enable_mw_ext_gal" in self.config["catalog_options"] and self.config["catalog_options"]["enable_mw_ext_gal"]: + MW_Av_arr = self.mw_ext.Av_from_Planck(ra=ra_arr, dec=dec_arr) + else: + MW_Av_arr = np.zeros(len(ra_arr)) + + for igals in range(ngals): + # # (TEST) + # if igals > 100: + # break + + param = self.initialize_param() + param['ra'] = ra_arr[igals] + param['dec'] = dec_arr[igals] + param['ra_orig'] = gals['ra'][igals] + param['dec_orig'] = gals['dec'][igals] + + # [TODO] get Milky Way extinction AVs + param['mw_Av'] = MW_Av_arr[igals] + + if not self.chip.isContainObj(ra_obj=param['ra'], dec_obj=param['dec'], margin=200): + continue + + # param['mag_use_normal'] = gals['mag_csst_%s'%(self.filt.filter_type)][igals] + # if self.filt.is_too_dim(mag=param['mag_use_normal'], margin=self.config["obs_setting"]["mag_lim_margin"]): + # continue + + param['z'] = gals['redshift'][igals] + param['model_tag'] = 'None' + param['g1'] = gals['shear'][igals][0] + param['g2'] = gals['shear'][igals][1] + param['kappa'] = gals['kappa'][igals] + param['e1'] = gals['ellipticity_true'][igals][0] + param['e2'] = gals['ellipticity_true'][igals][1] + + # For shape calculation + param['e1'], param['e2'], param['ell_total'] = self.rotate_ellipticity( + e1=gals['ellipticity_true'][igals][0], + e2=gals['ellipticity_true'][igals][1], + rotation=self.rotation, + unit='radians') + # param['ell_total'] = np.sqrt(param['e1']**2 + param['e2']**2) + if param['ell_total'] > 0.9: + continue + # phi_e = cmath.phase(complex(param['e1'], param['e2'])) + # param['e1'] = param['ell_total'] * np.cos(phi_e + 2*self.rotation) + # param['e2'] = param['ell_total'] * np.sin(phi_e + 2*self.rotation) + + param['e1_disk'] = param['e1'] + param['e2_disk'] = param['e2'] + param['e1_bulge'] = param['e1'] + param['e2_bulge'] = param['e2'] + + param['delta_ra'] = 0 + param['delta_dec'] = 0 + + # Masses + param['bulgemass'] = gals['bulgemass'][igals] + param['diskmass'] = gals['diskmass'][igals] + + param['size'] = gals['size'][igals] + if param['size'] > self.max_size: + self.max_size = param['size'] + + # Sersic index + param['disk_sersic_idx'] = 1. + param['bulge_sersic_idx'] = 4. + + # Sizes + param['bfrac'] = param['bulgemass'] / \ + (param['bulgemass'] + param['diskmass']) + if param['bfrac'] >= 0.6: + param['hlr_bulge'] = param['size'] + param['hlr_disk'] = param['size'] * (1. - param['bfrac']) + else: + param['hlr_disk'] = param['size'] + param['hlr_bulge'] = param['size'] * param['bfrac'] + + # SED coefficients + param['coeff'] = gals['coeff'][igals] + param['detA'] = gals['detA'][igals] + + # Others + param['galType'] = gals['type'][igals] + param['veldisp'] = gals['veldisp'][igals] + + # TEST no redening and no extinction + param['av'] = 0.0 + param['redden'] = 0 + + # TEMP + self.ids += 1 + param['id'] = '%06d' % (int(pix_id)) + \ + '%06d' % (cat_id) + '%08d' % (igals) + + # Is this an Quasar? + param['qsoindex'] = gals['qsoindex'][igals] + if param['qsoindex'] == -1: + param['star'] = 0 # Galaxy + param['agnsed_file'] = "" + obj = Galaxy(param, logger=self.logger) + else: + param_qso = copy.deepcopy(param) + param_qso['star'] = 2 # Quasar + param_qso['agnsed_file'] = agnsed_file + # First add QSO model + obj = Quasar(param_qso, logger=self.logger) + # Need to deal with additional output columns + obj.additional_output_str = self.add_fmt % (0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0, 0.) + self.objs.append(obj) + # Then add host galaxy model + param['star'] = 0 # Galaxy + param['agnsed_file'] = "" + obj = Galaxy(param, logger=self.logger) + + # Need to deal with additional output columns for (host) galaxy + obj.additional_output_str = self.add_fmt % (0., 0., 0., 0., 0., 0., + param['bulgemass'], param['diskmass'], param['detA'], + param['e1'], param['e2'], param['kappa'], param['g1'], param['g2'], param['size'], + param['galType'], param['veldisp']) + + self.objs.append(obj) + + def _load_stars(self, stars, pix_id=None): + nstars = len(stars['RA']) + # Apply astrometric modeling + ra_arr = stars["RA"][:] + dec_arr = stars["DEC"][:] + pmra_arr = stars['pmra'][:] + pmdec_arr = stars['pmdec'][:] + rv_arr = stars['RV'][:] + parallax_arr = stars['parallax'][:] + if self.config["obs_setting"]["enable_astrometric_model"]: + ra_list = ra_arr.tolist() + dec_list = dec_arr.tolist() + pmra_list = pmra_arr.tolist() + pmdec_list = pmdec_arr.tolist() + rv_list = rv_arr.tolist() + parallax_list = parallax_arr.tolist() + dt = datetime.utcfromtimestamp(self.pointing.timestamp) + date_str = dt.date().isoformat() + time_str = dt.time().isoformat() + ra_arr, dec_arr = on_orbit_obs_position( + input_ra_list=ra_list, + input_dec_list=dec_list, + input_pmra_list=pmra_list, + input_pmdec_list=pmdec_list, + input_rv_list=rv_list, + input_parallax_list=parallax_list, + input_nstars=nstars, + input_x=self.pointing.sat_x, + input_y=self.pointing.sat_y, + input_z=self.pointing.sat_z, + input_vx=self.pointing.sat_vx, + input_vy=self.pointing.sat_vy, + input_vz=self.pointing.sat_vz, + input_epoch="J2000", + input_date_str=date_str, + input_time_str=time_str + ) + for istars in range(nstars): + # # (TEST) + # if istars > 100: + # break + + param = self.initialize_param() + param['ra'] = ra_arr[istars] + param['dec'] = dec_arr[istars] + param['ra_orig'] = stars["RA"][istars] + param['dec_orig'] = stars["DEC"][istars] + param['pmra'] = pmra_arr[istars] + param['pmdec'] = pmdec_arr[istars] + param['rv'] = rv_arr[istars] + param['parallax'] = parallax_arr[istars] + if not self.chip.isContainObj(ra_obj=param['ra'], dec_obj=param['dec'], margin=200): + continue + param['mag_use_normal'] = stars['app_SDSS_g'][istars] + self.ids += 1 + + param['id'] = '%06d' % (int(pix_id)) + '%08d' % (istars) + # param['sed_type'] = istars + # param['model_tag'] = '' + param['teff'] = stars['teff'][istars] + param['logg'] = stars['grav'][istars] + param['feh'] = stars['z_met'][istars] + param['stellarMass'] = stars['mass'][istars] + + param['av'] = stars['AV'][istars] + param['DM'] = stars['DM'][istars] + # param['z_met'] = stars['z_met'][istars] + + param['z'] = 0.0 + param['star'] = 1 # Star + + try: + obj = Star(param, logger=self.logger) + except Exception as e: + print(e) + + # Append additional output columns to the .cat file + obj.additional_output_str = self.add_fmt % (param["av"], param['stellarMass'], param['DM'], param['teff'], param['logg'], param['feh'], + 0., 0., 0., 0., 0., 0., 0., 0., 0., -1, 0.) + + self.objs.append(obj) + + def free_mem(self, **kward): + self.starDDL.freeGlobeData() + del self.starDDL + + def _load(self, **kwargs): + self.objs = [] + self.ids = 0 + + if "star_cat" in self.config["catalog_options"]["input_path"] and self.config["catalog_options"]["input_path"]["star_cat"] and not self.config["catalog_options"]["galaxy_only"]: + star_cat = h5.File(self.star_path, 'r')['star_catalog'] + for pix in self.pix_list: + try: + stars = star_cat[str(pix)] + self._load_stars(stars, pix_id=pix) + del stars + except Exception as e: + self.logger.error(str(e)) + # print(e) + + if "galaxy_cat" in self.config["catalog_options"]["input_path"] and self.config["catalog_options"]["input_path"]["galaxy_cat"] and not self.config["catalog_options"]["star_only"]: + for pix in self.pix_list: + try: + bundleID = get_bundleIndex(pix) + bundle_file = "galaxies_C10_bundle{:06}.h5".format(bundleID) + file_path = os.path.join(self.galaxy_path, bundle_file) + gals_cat = h5.File(file_path, 'r')['galaxies'] + gals = gals_cat[str(pix)] + + # Get corresponding AGN SED file + agnsed_file = get_agnsed_file(bundle_file, self.config) + agnsed_path = os.path.join(self.AGN_SED_path, agnsed_file) + self.agn_seds[agnsed_file] = fits.open(agnsed_path)[0].data + self._load_gals(gals, pix_id=pix, cat_id=bundleID, agnsed_file=agnsed_file) + del gals + except Exception as e: + traceback.print_exc() + self.logger.error(str(e)) + print(e) + + if self.logger is not None: + self.logger.info("maximum galaxy size: %.4f" % (self.max_size)) + self.logger.info("number of objects in catalog: %d" % + (len(self.objs))) + else: + print("number of objects in catalog: ", len(self.objs)) + + def load_sed(self, obj, **kwargs): + if obj.type == 'star': + # _, wave, flux = tag_sed( + # h5file=self.tempSED_star, + # model_tag=obj.param['model_tag'], + # teff=obj.param['teff'], + # logg=obj.param['logg'], + # feh=obj.param['feh'] + # ) + wave, flux = self._interp_star_sed(obj) + elif obj.type == 'galaxy' or obj.type == 'quasar': + factor = 10**(-.4 * self.cosmo.distmod(obj.z).value) + if obj.type == 'galaxy': + flux = np.matmul(self.pcs, obj.coeff) * factor + # if np.any(flux < 0): + # raise ValueError("Glaxy %s: negative SED fluxes"%obj.id) + flux[flux < 0] = 0. + sedcat = np.vstack((self.lamb_gal, flux)).T + sed_data = getObservedSED( + sedCat=sedcat, + redshift=obj.z, + av=obj.param["av"], + redden=obj.param["redden"] + ) + wave, flux = sed_data[0], sed_data[1] + elif obj.type == 'quasar': + flux = self.agn_seds[obj.agnsed_file][int( + obj.qsoindex)] * 1e-17 + flux[flux < 0] = 0. + wave = self.lamb_gal * (1.0 + obj.z) + else: + raise ValueError("Object type not known") + speci = interpolate.interp1d(wave, flux, fill_value=0., bounds_error=False) + lamb = np.arange(2000, 11001+0.5, 0.5) + y = speci(lamb) + + # [TODO] Apply Milky Way extinction + if obj.type != 'star' and ("enable_mw_ext_gal" in self.config["catalog_options"] and self.config["catalog_options"]["enable_mw_ext_gal"]): + y = self.mw_ext.apply_extinction(y, Av=obj.mw_Av) + + # erg/s/cm2/A --> photon/s/m2/A + all_sed = y * lamb / (cons.h.value * cons.c.value) * 1e-13 + sed = Table(np.array([lamb, all_sed]).T, names=('WAVELENGTH', 'FLUX')) + # if obj.type == 'quasar': + # # integrate to get the magnitudes + # sed_photon = np.array([sed['WAVELENGTH'], sed['FLUX']]).T + # sed_photon = galsim.LookupTable(x=np.array(sed_photon[:, 0]), f=np.array( + # sed_photon[:, 1]), interpolant='nearest') + # sed_photon = galsim.SED( + # sed_photon, wave_type='A', flux_type='1', fast=False) + # interFlux = integrate_sed_bandpass( + # sed=sed_photon, bandpass=self.filt.bandpass_full) + # obj.param['mag_use_normal'] = getABMAG( + # interFlux, self.filt.bandpass_full) + # # mag = getABMAG(interFlux, self.filt.bandpass_full) + # # print("mag diff = %.3f"%(mag - obj.param['mag_use_normal'])) + + # integrate to get the magnitudes + if obj.type == 'quasar' or obj.type == 'galaxy': + sed_photon = np.array([sed['WAVELENGTH'], sed['FLUX']]).T + sed_photon = galsim.LookupTable(x=np.array(sed_photon[:, 0]), f=np.array( + sed_photon[:, 1]), interpolant='nearest') + sed_photon = galsim.SED( + sed_photon, wave_type='A', flux_type='1', fast=False) + interFlux = integrate_sed_bandpass( + sed=sed_photon, bandpass=self.filt.bandpass_full) + obj.param['mag_use_normal'] = getABMAG( + interFlux, self.filt.bandpass_full) + # mag = getABMAG(interFlux, self.filt.bandpass_full) + # print("mag diff = %.3f"%(mag - obj.param['mag_use_normal'])) + del wave + del flux + return sed diff --git a/catalog/C9_Catalog.py b/catalog/C9_Catalog.py index 146205dc95ff2868ce652e84679c504ed3fbf71a..6f3a7f420bb24f8f3b46585db85e81ac268d13c9 100644 --- a/catalog/C9_Catalog.py +++ b/catalog/C9_Catalog.py @@ -380,7 +380,7 @@ class Catalog(CatalogBase): obj = Galaxy(param, logger=self.logger) # Need to deal with additional output columns for (host) galaxy - obj.additional_output_str = self.add_fmt % (0., 0., 0., 0., 0., 0., + obj.additional_output_str = self.add_fmt % (param['mw_Av'], 0., 0., 0., 0., 0., param['bulgemass'], param['diskmass'], param['detA'], param['e1'], param['e2'], param['kappa'], param['g1'], param['g2'], param['size'], param['galType'], param['veldisp']) @@ -469,6 +469,11 @@ class Catalog(CatalogBase): self.objs.append(obj) + def free_mem(self, **kward): + if not self.config["catalog_options"]["galaxy_only"]: + self.starDDL.freeGlobeData() + del self.starDDL + def _load(self, **kwargs): self.objs = [] self.ids = 0 diff --git a/catalog/Catalog_example.py b/catalog/Catalog_example.py index 4b51f0a2dd1213294ffcc21f50fe780250c32067..d3a6f28c1e050bf95fb4c4c1e9d8cf36671c5179 100644 --- a/catalog/Catalog_example.py +++ b/catalog/Catalog_example.py @@ -72,7 +72,7 @@ class Catalog(CatalogBase): This is a must implemented method which is used to read in all objects, and then convert them to observation_sim.mock_objects (Star, Galaxy, or Quasar). - Currently, + Currently, the model of observation_sim.mock_objects.Star class requires: param["star"] : int specify the object type: 0: galaxy, 1: star, 2: quasar @@ -84,7 +84,7 @@ class Catalog(CatalogBase): Declination (in degrees) param["mag_use_normal"]: float the absolute magnitude in a particular filter - NOTE: if that filter is not the corresponding CSST filter, the + NOTE: if that filter is not the corresponding CSST filter, the load_norm_filt(obj) function must be implemented to load the filter throughput of that particular photometric system @@ -99,7 +99,7 @@ class Catalog(CatalogBase): Declination (in degrees) param["mag_use_normal"]: float the absolute magnitude in a particular filter - NOTE: if that filter is not the corresponding CSST filter, the + NOTE: if that filter is not the corresponding CSST filter, the load_norm_filt(obj) function must be implemented to load the filter throughput of that particular photometric system param["bfrac"] : float diff --git a/catalog/data/starSpecInterp.so b/catalog/data/starSpecInterp.so old mode 100755 new mode 100644 index 150d42c5d743164a22eef536f846317e85aae387..11899b46080fb4434dc6b8a20c89a23fd80d94dd Binary files a/catalog/data/starSpecInterp.so and b/catalog/data/starSpecInterp.so differ diff --git a/catalog/testCat_fits.py b/catalog/testCat_fits.py index 6ae1bada7eb8b6ff34db22ec4aaa6f725de69219..9717b22ab0315d594491a9ca54ac39eaf2686dbf 100644 --- a/catalog/testCat_fits.py +++ b/catalog/testCat_fits.py @@ -177,7 +177,7 @@ class Catalog(CatalogBase): # param["CSSTmag"]= True # param["mag_r"] = 20. # param[''] - ### more keywords for stamp### + # more keywords for stamp# param['image'] = hdu[0].data param['image'] = param['image']/(np.sum(param['image'])) obj = Stamp(param) diff --git a/config/config_overall.yaml b/config/config_overall.yaml index d22dc6847dd3ee0ade13373233a91e38ec1f5770..97ac2bc26d5f4f4e1e9f5e84696dd2405dbc4bab 100644 --- a/config/config_overall.yaml +++ b/config/config_overall.yaml @@ -12,6 +12,7 @@ # ok to pass either way or both, as long as they are consistent work_dir: "/public/home/fangyuedong/project/workplace/" run_name: "ext_on" +data_set: "csst-msc-c9-25sqdeg-v_test" # Project cycle and run counter are used to name the outputs project_cycle: 9 diff --git a/config/obs_config_SCI.yaml b/config/obs_config_SCI.yaml index ef82bcdc6c0e17cc788ca396dd4fa3be087a159a..fb8f538b2bbdfd3b42ec0cacb5e6c3538a014fa0 100644 --- a/config/obs_config_SCI.yaml +++ b/config/obs_config_SCI.yaml @@ -62,10 +62,10 @@ call_sequence: hot_pixels: YES dead_pixels: YES bad_columns: YES - # Apply response nonlinearity - nonlinearity: {} # Apply CCD Saturation & Blooming blooming: {} + # Apply response nonlinearity + nonlinearity: {} # Run CTE simulation # CTE_effect: {} # Add prescan and overscan @@ -74,6 +74,8 @@ call_sequence: # Add bias bias: bias_16channel: YES + # Add cross-talk + cross_talk: {} # Add readout noise readout_noise: {} # Apply gain diff --git a/observation_sim/ObservationSim.py b/observation_sim/ObservationSim.py index cb060ab4d65520e8a3ae76b5b5f678a88cfe43d0..7d35a6680bb0f8e890b3d06f6b0a26446937d373 100755 --- a/observation_sim/ObservationSim.py +++ b/observation_sim/ObservationSim.py @@ -25,7 +25,7 @@ class Observation(object): def prepare_chip_for_exposure(self, chip, ra_cen, dec_cen, pointing, wcs_fp=None, slsPSFOptim=False): # Get WCS for the focal plane - if wcs_fp == None: + if wcs_fp is None: wcs_fp = self.focal_plane.getTanWCS( ra_cen, dec_cen, pointing.img_pa, chip.pix_scale) @@ -62,6 +62,8 @@ class Observation(object): # Get flat, shutter, and PRNU images chip.flat_img, _ = chip_utils.get_flat( img=chip.img, seed=int(self.config["random_seeds"]["seed_flat"])) + if chip.chipID <= 30: + chip.flat_img = chip.flat_img*chip_utils.get_innerflat(chip=chip) if chip.chipID > 30: chip.shutter_img = np.ones_like(chip.img.array) else: @@ -154,13 +156,12 @@ class Observation(object): chip_output.Log_error(e) chip_output.Log_error("Failed simulation on step: %s" % (step)) break - - 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))) del chip.img del chip.flat_img del chip.prnu_img del chip.shutter_img + 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))) def runExposure_MPI_PointingList(self, pointing_list, chips=None): comm = MPI.COMM_WORLD @@ -239,5 +240,6 @@ class Observation(object): "finished running chip#%d..." % (chip.chipID)) for handler in chip_output.logger.handlers[:]: chip_output.logger.removeHandler(handler) + del chip_output gc.collect() process_counter += nchips_per_fp diff --git a/observation_sim/_util.py b/observation_sim/_util.py index 47c91ce66d01ce7c06b119ee02822b112dc5f7d7..7c2312750dce7ec35ea665ec67432889ffdcfd4f 100755 --- a/observation_sim/_util.py +++ b/observation_sim/_util.py @@ -26,7 +26,7 @@ def parse_args(): return parser.parse_args() -def generate_pointing_list(config, pointing_filename=None, data_dir=None): +def generate_pointing_list(config, pointing_filename=None, data_dir=None, dataset=''): pointing_list = [] # Only valid when the pointing list does not contain time stamp column @@ -63,7 +63,8 @@ def generate_pointing_list(config, pointing_filename=None, data_dir=None): continue line = line.strip() columns = line.split() - pointing = Pointing(obs_config_file=obs_config_file) + pointing = Pointing( + obs_config_file=obs_config_file, dataset=dataset) pointing.read_pointing_columns(columns=columns, id=ipoint) t += delta_t * 60. pointing_list.append(pointing) @@ -83,7 +84,8 @@ def generate_pointing_list(config, pointing_filename=None, data_dir=None): timestamp=t, exp_time=exp_time, pointing_type='SCI', - obs_config_file=obs_config_file + obs_config_file=obs_config_file, + dataset=dataset ) t += delta_t * 60. pointing_list.append(pointing) diff --git a/observation_sim/astrometry/Astrometry_util.py b/observation_sim/astrometry/Astrometry_util.py index dba4466365299b584422dbad4833e342e25bcac8..bdfe613905658df3ec967f8ce80e06b1ce2c34be 100644 --- a/observation_sim/astrometry/Astrometry_util.py +++ b/observation_sim/astrometry/Astrometry_util.py @@ -12,8 +12,8 @@ def checkInputList(input_list, n): if not isinstance(input_list, list): raise TypeError("Input type is not list!", input_list) for i in input_list: - if type(i) != type(1.1): - if type(i) != type(1): + if type(i) is not type(1.1): + if type(i) is not type(1): raise TypeError( "Input list's element is not float or int!", input_list) if len(input_list) != n: diff --git a/observation_sim/config/ChipOutput.py b/observation_sim/config/ChipOutput.py index 7db5e12d465534523c040a7e1d9e66330ece659e..9d66359bb92b23561ad67813197c8bc6f041f96a 100755 --- a/observation_sim/config/ChipOutput.py +++ b/observation_sim/config/ChipOutput.py @@ -28,7 +28,8 @@ class ChipOutput(object): 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=self.chip_label) + chip_name=self.chip_label, + dataset=pointing.dataset) obs_id = _util.get_obs_id(img_type=self.pointing_type, project_cycle=config["project_cycle"], run_counter=config[ "run_counter"], pointing_id=pointing.obs_id, pointing_type_code=pointing.pointing_type_code) diff --git a/observation_sim/config/Pointing.py b/observation_sim/config/Pointing.py index 1b399596e8789ef8de1e8a78a2d437cca88565af..36766a10c740676b44b73d73631d56ed06aa429d 100644 --- a/observation_sim/config/Pointing.py +++ b/observation_sim/config/Pointing.py @@ -10,7 +10,7 @@ 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): + 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, dataset='csst-msc-c9-25sqdeg-v3'): self.id = id self.ra = ra self.dec = dec @@ -41,6 +41,7 @@ class Pointing(object): 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"]) + self.dataset = dataset def get_full_depth_exptime(self, filter_type): if self.survey_field_type == 'WIDE': @@ -88,7 +89,7 @@ class Pointing(object): self.exp_time = float(columns[18]) is_deep = float(columns[19]) # [TODO] Can also define other survey types - if is_deep != -1.0: + if is_deep == 2: self.survey_field_type = "DEEP" if not self.obs_config_file: @@ -114,12 +115,13 @@ class Pointing(object): 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_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_prefix = self.pointing_type_code+self.obs_id self.output_dir = os.path.join(run_dir, self.output_prefix) if not os.path.exists(self.output_dir): try: diff --git a/observation_sim/config/__init__.py b/observation_sim/config/__init__.py index bf03e61f5078114b03a38a19d2b43fae72b13341..6d22c04fcbae1968fd952e7ac4b8110a23d05667 100644 --- a/observation_sim/config/__init__.py +++ b/observation_sim/config/__init__.py @@ -1,2 +1,2 @@ from .ChipOutput import ChipOutput -from .Pointing import Pointing \ No newline at end of file +from .Pointing import Pointing diff --git a/observation_sim/config/header/ImageHeader.py b/observation_sim/config/header/ImageHeader.py index 25a2867df16aff0fb6d5c25942d9f757b9803371..ef57c8650a4d66a032799d4eee4ece01b250e3b9 100644 --- a/observation_sim/config/header/ImageHeader.py +++ b/observation_sim/config/header/ImageHeader.py @@ -355,7 +355,7 @@ def WCS_def(xlen=9216, ylen=9232, gapy=898.0, gapx1=534, gapx2=1309, ra_ref=60, # TODO project_cycle is temporary, is not in header defined, delete in future -def generatePrimaryHeader(xlen=9216, ylen=9232, pointing_id='00000001', pointing_type_code='101', ra=60, dec=-40, pixel_scale=0.074, time_pt=None, im_type='SCI', 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, pointing_id='00000001', pointing_type_code='101', ra=60, dec=-40, pixel_scale=0.074, time_pt=None, im_type='SCI', exptime=150., sat_pos=[0., 0., 0.], sat_vel=[0., 0., 0.], project_cycle=6, run_counter=0, chip_name="01", obstype='WIDE', dataset='csst-msc-c9-25sqdeg-v3'): # array_size1, array_size2, flux, sigma = int(argv[1]), int(argv[2]), 1000.0, 5.0 @@ -416,8 +416,9 @@ def generatePrimaryHeader(xlen=9216, ylen=9232, pointing_id='00000001', pointing # # OBS_id = '1'+ obs_type[im_type] + str(int(project_cycle)) + pointNum.rjust(7,'0') # OBS_id = '1'+ obs_type[im_type] + str(int(project_cycle)) + str(int(run_counter)).rjust(2, '0') + pointNum.rjust(5,'0') - OBS_id = get_obs_id(img_type=im_type, project_cycle=project_cycle, run_counter=run_counter, - pointing_id=pointing_id, pointing_type_code=pointing_type_code) + # OBS_id = get_obs_id(img_type=im_type, project_cycle='project_cycle', run_counter=run_counter, + # pointing_id=pointing_id, pointing_type_code=pointing_type_code) + OBS_id = pointing_type_code+pointing_id # h_prim['OBJECT'] = str(int(project_cycle)) + pointNum.rjust(7, '0') h_prim['OBJECT'] = pointing_id @@ -429,8 +430,9 @@ def generatePrimaryHeader(xlen=9216, ylen=9232, pointing_id='00000001', pointing # 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'] = im_type - + # h_prim['FILETYPE'] = im_type + h_prim['OBSTYPE'] = obstype + h_prim['DATASET'] = dataset co = coord.SkyCoord(ra, dec, unit='deg') ra_hms = format(co.ra.hms.h, '02.0f') + format(co.ra.hms.m, @@ -469,7 +471,7 @@ def generatePrimaryHeader(xlen=9216, ylen=9232, pointing_id='00000001', pointing end_time_str[11:13] + end_time_str[14:16] + end_time_str[17:19] # h_prim['FILENAME'] = 'CSST_MSC_MS_' + im_type + '_' + file_start_time + '_' + file_end_time + '_' + OBS_id + '_' + CCDID[ # k - 1].rjust(2, '0') + '_L0_V01' - h_prim['FILENAME'] = 'CSST_MSC_MS_' + h_prim['FILETYPE'] + '_' + \ + h_prim['FILENAME'] = 'CSST_MSC_MS_' + im_type + '_' + \ file_start_time + '_' + file_end_time + \ '_' + OBS_id + '_' + chip_name + '_L0_V01' @@ -531,8 +533,8 @@ def generateExtensionHeader(chip, xlen=9216, ylen=9232, ra=60, dec=-40, pa=-23.4 # h_ext['CCDCHIP'] = CCDID[k - 1].rjust(2, '0') # h_ext['CCDLABEL'] = filters[k-1] + '-' + filterID[k-1] # h_ext['FILTER'] = filters[k-1] - h_ext['CHIPID'] = str(chip.chipID).rjust(2, '0') - h_ext['CHIPLABL'] = chip.chip_name + h_ext['DETECTOR'] = str(chip.chipID).rjust(2, '0') + h_ext['DETLABEL'] = chip.chip_name h_ext['FILTER'] = chip.filter_type h_ext['NAXIS1'] = xlen h_ext['NAXIS2'] = ylen diff --git a/observation_sim/config/header/__init__.py b/observation_sim/config/header/__init__.py index db07f26a1fca8b82fd63fa1fc526b5547d47b120..bbe9ba53b9461420ef1a32bbc53e54ccc77cc4c0 100755 --- a/observation_sim/config/header/__init__.py +++ b/observation_sim/config/header/__init__.py @@ -1,2 +1,2 @@ from .ImageHeader import generatePrimaryHeader -from .ImageHeader import generateExtensionHeader \ No newline at end of file +from .ImageHeader import generateExtensionHeader diff --git a/observation_sim/config/header/csst_msc_l0_ms.fits b/observation_sim/config/header/csst_msc_l0_ms.fits index 3866cdb8f45b211e837aa7746513470cc900d362..7fd146c316df19a31c2481803db5d1f570b62862 100644 --- a/observation_sim/config/header/csst_msc_l0_ms.fits +++ b/observation_sim/config/header/csst_msc_l0_ms.fits @@ -1,4 +1,4 @@ -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 DATE = '2024-08-17T06:05:12.0' / written date (yyyy-mm-ddThh:mm:ss.s) FILENAME= 'CSST_MSC_MS_SCI_20240817060512_20240817060742_10160100000_09_L0_V01' FILETYPE= 'SCI ' / observation type TELESCOP= 'CSST ' / telescope name INSTRUME= 'MSC ' / instrument name RADECSYS= 'ICRS ' / coordinate system of the object EQUINOX = 2000.0 FITSSWV = '2.1.0_20231208_wcl-5' / FITS creating software version COMMENT ========================================================================COMMENT OBJECT INFORMATION COMMENT ========================================================================OBJECT = '60000000' / object name TARGET = '161953.0+395345' / target name (hhmmss.s+ddmmss) OBSID = '10160100000' / observation ID RA_OBJ = 244.972773 / object RA (deg) DEC_OBJ = 39.895901 / object Dec (deg) COMMENT ========================================================================COMMENT TELESCOPE INFORMATION COMMENT ========================================================================REFFRAME= 'CSSTGSC-1.0' / guiding catalog version DATE-OBS= '2024-08-17T06:05:12.0' / observation date (yyyy-mm-ddThh:mm:ss.s) SATESWV = '0001 ' / satellite software version EXPSTART= 60539.25361 / exposure start time (MJD) CABSTART= 60539.25361 / first cabin time after exposure start (MJD) SUNANGL0= 50.0 / angle between the Sun and opt axis at CABSTART MOONANG0= 30.0 / angle between the Moon and opt axis at CABSTARTTEL_ALT0= 20.0 / angle between opt axis and Elimb at CABSTART POS_ANG0= 20.0 / angle between y axis and North Pole at CABSTARTPOSI0_X = -2218.3663 / orbital position in X at CABSTART (km) POSI0_Y = -5817.8559 / orbital position in Y at CABSTART (km) POSI0_Z = 2597.9467 / orbital position in Z at CABSTART (km) VELO0_X = 5.058199026407237 / orbital velocity in X at CABSTART (km/s) VELO0_Y = -3.85818799871231 / orbital velocity in Y at CABSTART (km/s) VELO0_Z = -4.322908628205369 / orbital velocity in Z at CABSTART (km/s) EULER0_1= 0.0 / Euler angle 1 at CABSTART (deg) EULER0_2= 0.0 / Euler angle 2 at CABSTART (deg) EULER0_3= 0.0 / Euler angle 3 at CABSTART (deg) RA_PNT0 = 244.972773 / pointing RA at CABSTART (deg) DEC_PNT0= 39.895901 / pointing Dec at CABSTART (deg) EXPEND = 60539.25535 / exposure end time (MJD) CABEND = 60539.25535 / first cabin time after exposure end (MJD) SUNANGL1= 50.0 / angle between the Sun and opt axis at CABEND MOONANG1= 30.0 / angle between the 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 North Pole at CABEND POSI1_X = 0.0 / orbital position in X at CABEND (km) POSI1_Y = 0.0 / orbital position in Y at CABEND (km) POSI1_Z = 0.0 / orbital position in Z at CABEND (km) VELO1_X = 0.0 / orbital velocity in X at CABEND (km/s) VELO1_Y = 0.0 / orbital velocity in Y at CABEND (km/s) VELO1_Z = 0.0 / orbital velocity in Z at CABEND (km/s) EULER1_1= 0.0 / Euler angle 1 at CABEND (deg) EULER1_2= 0.0 / Euler angle 2 at CABEND (deg) EULER1_3= 0.0 / Euler angle 3 at CABEND (deg) RA_PNT1 = 244.972773 / pointing RA at CABEND (deg) DEC_PNT1= 39.895901 / pointing Dec at CABEND (deg) EPOCH = 2024.6 / equinox of pointing RA and Dec EXPTIME = 150.0 / exposure time (s) COMMENT ========================================================================COMMENT VERIFICATION INFORMATION COMMENT ========================================================================CHECKSUM= 'DhIlDgHkDgHkDgHk' / HDU checksum updated 2024-03-15T15:51:50 DATASUM = '0 ' / data unit checksum updated 2024-03-15T15:51:50 END XTENSION= 'IMAGE ' / image extension BITPIX = 16 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 20000 NAXIS2 = 4700 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups BSCALE = 1 BZERO = 32768 EXTNAME = 'IMAGE ' / extension name EXTVER = 1 / extension version number 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 (K) DETTEMP2= 173.0 / detector temperature at EXPEND (K) DETTEMP3= 173.0 / detector temperature at READT1 (K) DETSIZE = '9560x9264' / detector size DATASECT= '9216x9232' / data section PIXSCAL1= 0.074 / pixel scale for axis 1 (arcsec/pixel) PIXSCAL2= 0.074 / pixel scale for axis 2 (arcsec/pixel) PIXSIZE1= 10 / pixel size for axis 1 (micron) PIXSIZE2= 10 / pixel size for axis 2 (micron) CHIPID = '09 ' / chip ID CHIPLABL= 'r-1 ' / chip label FILTER = 'r ' / filter name NCHANNEL= 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 INFORMATION COMMENT ========================================================================WCSAXES = 2 / number of WCS axes CRPIX1 = 4600.694649742984 / x-coordinate of reference pixel CRPIX2 = 4604.383757040574 / y-coordinate of reference pixel CRVAL1 = 244.5872723135913 / value of reference pixel on axis 1 CRVAL2 = 39.68195466168969 / value of reference pixel on axis 2 CTYPE1 = 'RA---TAN' / type of RA WCS projection CTYPE2 = 'DEC--TAN' / type of Dec WCS projection CD1_1 = -6.8096767434962E-06 / transformation matrix element (deg/pix) CD1_2 = -1.9394088718019E-05 / transformation matrix element (deg/pix) CD2_1 = -1.9394079613547E-05 / transformation matrix element (deg/pix) CD2_2 = 6.81009530128767E-06 / transformation matrix element (deg/pix) COMMENT ========================================================================COMMENT READOUT INFORMATION COMMENT ========================================================================GAINLVL = '01' / gain level GAIN01 = 1.089977689498424 / gain (e-/ADU, channel 01) GAIN02 = 1.099175619835018 / gain (e-/ADU, channel 02) GAIN03 = 1.107649165474623 / gain (e-/ADU, channel 03) GAIN04 = 1.121722214644728 / gain (e-/ADU, channel 04) GAIN05 = 1.081220797968546 / gain (e-/ADU, channel 05) GAIN06 = 1.09456272850753 / gain (e-/ADU, channel 06) GAIN07 = 1.111430472148367 / gain (e-/ADU, channel 07) GAIN08 = 1.105606424366627 / gain (e-/ADU, channel 08) GAIN09 = 1.101874970320761 / gain (e-/ADU, channel 09) GAIN10 = 1.085064046871988 / gain (e-/ADU, channel 10) GAIN11 = 1.12218772525153 / gain (e-/ADU, channel 11) GAIN12 = 1.114934178226512 / gain (e-/ADU, channel 12) GAIN13 = 1.119336504852553 / gain (e-/ADU, channel 13) GAIN14 = 1.093036190194986 / gain (e-/ADU, channel 14) GAIN15 = 1.084334798104931 / gain (e-/ADU, channel 15) GAIN16 = 1.090894538052644 / gain (e-/ADU, channel 16) RON01 = 5.0 / readout noise (e-, channel 01) RON02 = 5.0 / readout noise (e-, channel 02) RON03 = 5.0 / readout noise (e-, channel 03) RON04 = 5.0 / readout noise (e-, channel 04) RON05 = 5.0 / readout noise (e-, channel 05) RON06 = 5.0 / readout noise (e-, channel 06) RON07 = 5.0 / readout noise (e-, channel 07) RON08 = 5.0 / readout noise (e-, channel 08) RON09 = 5.0 / readout noise (e-, channel 09) RON10 = 5.0 / readout noise (e-, channel 10) RON11 = 5.0 / readout noise (e-, channel 11) RON12 = 5.0 / readout noise (e-, channel 12) RON13 = 5.0 / readout noise (e-, channel 13) RON14 = 5.0 / readout noise (e-, channel 14) RON15 = 5.0 / readout noise (e-, channel 15) RON16 = 5.0 / readout noise (e-, channel 16) ROTIME0 = '2024-08-17T06:07:42.1' / readout start time (UTC) ROTIME1 = '2024-08-17T06:08:22.1' / readout end time (UTC) ROSPEED = 10.0 / readout speed (MHz) EXPTIME = 150.0 / exposure time (s) DARKTIME= 152.0 / dark current time (s) COMMENT ========================================================================COMMENT SHUTTER INFORMATION COMMENT ========================================================================SHTSTAT = T / shutter status, T=open, F=close SHTOPEN0= '2024-08-17T06:07:42.1' / shutter open time (start, UTC) SHTOPEN1= '2024-08-17T06:07:42.1' / shutter open time (end, UTC) SHTCLOS0= '2024-08-17T06:07:42.1' / shutter close time (start, UTC) SHTCLOS1= '2024-08-17T06:07:42.1' / shutter close time (end, UTC) COMMENT ========================================================================COMMENT LED INFORMATION COMMENT ========================================================================LEDFLAG = 'main ' / main/backup LED LEDSTAT = '00000000000000' / LED status LEDT01 = 0.0 / LED01 flash time (ms) LEDT02 = 0.0 / LED02 flash time (ms) LEDT03 = 0.0 / LED03 flash time (ms) LEDT04 = 0.0 / LED04 flash time (ms) LEDT05 = 0.0 / LED05 flash time (ms) LEDT06 = 0.0 / LED06 flash time (ms) LEDT07 = 0.0 / LED07 flash time (ms) LEDT08 = 0.0 / LED08 flash time (ms) LEDT09 = 0.0 / LED09 flash time (ms) LEDT10 = 0.0 / LED10 flash time (ms) LEDT11 = 0.0 / LED11 flash time (ms) LEDT12 = 0.0 / LED12 flash time (ms) LEDT13 = 0.0 / LED13 flash time (ms) LEDT14 = 0.0 / LED14 flash time (ms) LEDTEMP = 173.0 / LED temperature (K) COMMENT ========================================================================COMMENT VERIFICATION INFORMATION COMMENT ========================================================================CHECKSUM= 'gWAehU4dgU9dgU9d' / HDU checksum updated 2024-03-15T15:51:51 DATASUM = '3747953988' / data unit checksum updated 2024-03-15T15:51:51 END ÅÄÅÍÍÁ¾È»ÅÇȾÃÁ¼ÃÇÈÃÇÌÁÃÂËÊÚÓÙéæêàéèæÞáÝêæáåå×ìÞäôÓæåâëÛàßÝäèÛîíàßáÝïâÓæ×äãßãæâæêàØÜêâêäéäâãëåééÜêãááßáÙáçÚãàëéÛãÛæêÞÚÓáéðíçëèêÝæçîÜòèççáèâáçîèàåßÚæäàëàÚâÜÞÛãèãßååÚêæíåÞ×ãæíäêçñèæÝãíæïìæåçâ×ÝæÛÕáâÜÒâëâàÕäâÚìîððíääííãçéàâêéßáðöäÜèàêéÞÝçäÙäæÞéáÞãÞàäÛé×âäÜäáØÝèâàÚãÞãîëâçèâäâîàòëßéÙêàèíæàåÞÙãââïåèæÚßçéÖæêÔÝçéçëêæßÕéäàÓäâãæëéæâ×ÖåæèäÝßéæãÐàáçßëÝëÝÚÞÞäæåÕíàÒÖààìéíäêôìðô‚ óìðôïåÑÝéß÷àÛÞÙßëçðïèëñàãÝèêîêÜãåçäáÕáëêÞçòâææêÜîòëØèåèáäáíäïåèäìèßàãßßåÚâñææìæèëääñõêêàÞóðÓÚéÙàÒÞéíæÞíãæââÜáèÝàÓèñáÔÙáàáççáÛáéããæáÜÜáçï×áãìâäàåæîëäÜéäÖÝáâÙáðéØêäèÜâãìííìÛÝÜïâßêæáçèêéäãâÖôáðééíñòîëñêìéñìéæááåÛîäåâßÞÝêàÝâåææÒÞèÛíÛÜèñæéßáßÜêØÜãêããææðéíÙæçâÚâßíðäàí×îÞäðØààÝçæßÞéããÕæâæÚáåÙéääæáÝØàßéåçâéÚÝéîÔæéìââÛÞèááÔâèØæçÙäêáçàâãæÚØâàåÞÚãÙâèå×àßâÝéàÛâÛéäàëàÝãàéâçßÚÛèäæÜçØõÚÞåæÓäçÙëäÝàààææßÝìêäïåâÙæ×ãáåèðßéâêãçæÙèçÞÜ×ÚÝðÜãàÞåâçÐíØÞèãÛçãÛçÕØæÕåðäîïââãæÜÜÞâãâåâÝðìÚßçèÛØÚêõêæëîÕëëÞââ÷ä䨂xåïÛÛåÚêìÙâáàâÞàëÚéîåáâÜôØìØÚÚåÝàÞëÚÕòåãëÚäôàßÝãäåéÝàáæêëâÞèÞâÛéàêâÝèæßâáßååäâòòáéäáéçáâàãØëÒëáèåßëèÞìéåæÔÞåäííçÞèæëâïáÛÞáèáì÷ãÝäààßèñíðã×çÞòèÝëæÞéßçÚߨßãÜÞãàÜàèêêìîèÝ×ðßèåÛãéçêæãÕîÝÛàÞèñÙáäééåäßêîíâØåéâÜÞìåãåéïÞáàñÓçïÏîØáëñçÞçåØçÝÚîãÓæáâçåèÛÝèÛÛÚíàÛäçÙêææåéäâèßçæÞØòèÚáîéïâæåäâéëßåÝæâããáëìåàáÞÚåéàÙóàãßéâæ×éãæßêçáàåæãâá×çéååØÃÂÂÅÁÄÇÄÃÅÌÁ½ÃÁÆËÆÄÊÇÅÄŽÁÅÞÅÇÉÆÉÌÁÈÆÉ½ÂÉÅÆÁÈÅÐÂÁÀËÄÈÂÄÈÄÂÊÆÈÅü¿ÅÆÂÈÄÉÇÉÁÍÁÐÍÇÇÍÊÄÊÇÅÊÉÑÉÊÐÌÄËÐàèèëãðààêèßñêðøâîõïÞæÚêñéëæìãêæïêûéüÞòïèñâÝûääñáïÕßæíîàçëíßìßêîñæëìßéæÞâìèçôîÞÜåÞéëîäíæîéíìíÝàãçÛÜëçèäèÞåæåãçàìèáåßëãçäëßå‚éèäæõèæûÙéäïáãëìßíÛèóôèêñèìæäâìëêÝèíêëàééïëéðîïàñèææâåãîä×éÔÚäêßãìïîãâàéèêêëçêéï‚‚‚ ‚ðæççãäêäéìèÞéðïäîãìîòäÛäéäæèýäàðêóëëíñáîæêÜäëãééæãêêÝÞæåãëðãÜìîàìðíçóóÖßãåìîíìïïáàççëìáóüæåàèïóÞÞêìêéÛêùæìêäïñöößçäáìììëëçéæáéâåÞåææçóéôìîçôçåàêìùæåìàØîâîèéõðìíëêãèïäùëèêìççßõåõêÞÞëêôíäáêæåëêêéêåëåæóåëåèéðççáÕçøëçßÚêéêïäÜßéõèÝæãêêãéæèæäëàãÝæãïíçàëáîåááñöõÞÞëàßßóÜçëéÞêîêæ÷ðèñáðîáøÝêäêçÙìäæâæáÜãêâæîãßçåÝòíñìñÝæÞèíàéÞâîéçéäÛêìèäïãæìîæîçÞìûèéííæàäêêæÜåââØêãìÝáã×ìèééßïçèäçãêâçíñÞìîíàçÝäáïêåèêçëÞíÔäéøæáðâäëêçàáäëàõõéãêìçæáçíçòâãâÜæëääñèíêæóÜåëåßæøãðèéäêæììæäåêñèå×ßÜòçèñéßïø÷ÜõïäèäîèæïßçâèîáÖäÞÜæìòâñåõÝéëåéëáÙããíçéìÞçåäîæíçéñâàîåæçÞðååÞçäåìêæçíèåçòøäíÝê×íì׿êÜçåëîÝîêÛéÛäèáñãåßéäìëêêåûççÛñ×ëéïæçîßßæöæîàåÜéôì‚‚$‚ôêîåÞáçâçÞàññãéãÝëâèêéøñãíéåçíåøæßäõæãæÞØëìßõáåäïëçîâßÞéÙáôæíñõåêèç×êéïãíÞíüîäòêäñÜÝõòëãäòñôáçìäàêäêåÜéèÚßòäâêñØáëßîëôúçðáíïðÚå÷‚cãÝêìäïáçèåçöóáêâëëìæîêÚðïåðåäåâëçñéåãäþâåæûîòèðäëãîáóíèæ÷ìæáîêäíáèßçïìÛëàâëêíãåëãææéÓäëìæîóëååãîáôðçãéîîèëããåöçæåæçñéíÙäÝçèäèêîáççòÞïäââëëü÷ÞæéåéèéâèãíéëáàÝèÜÛàáäíðçðîìöçåâåëëÞáçâóñðââÞæáãåçìÚïèëäëðåçêãèáôôÅѸÌÄÆÌÇÌÅÁÑÁËÒÍËÈËÉÈÏÈÄÈÑÂÉÉÎÌÂÔÊÊÊÌÊÈÈÄÅÈÁÏÁÊÈÅÉÂËÍÆÌÇËÆÆÇÆÉÊÏÆËÐÄËÊü¼¶½À¾¿µ¾½·¶½º½¸¸¿ºÀµ¾À¾¸Á»×ÙÕÓÚÉ×ÏÏÑÜàØÞÎÐÕÔÕÜÚÝÑßËØÜÛÞØÞÓßÓßßÕàÖÝÓÞÚÓÔÔÐÒÓéÒÜØÕÚÏØÖ×ÍÕãÐ×Ï×ÑÙÞÛÒÚ×ÕÜÏÕÐÔÜ×ÍÖÔÖßéÑÛÒÒÕÏÕÔÕÌËßÚÊÒßáÏÚÑÝáÝÙÓÌÝßàÜÞÛÖÝÝßÒØÖÖÚÑÜÛÖËãÏÍÔÔÚÏ×ÚÐéØÌæéÞÔÔÙߨ×רØÐÒ×Ý×ÖÝËâÐÑÑÔÚÖÔÛÊØÐ×áæäßßÚÎÙáÝáÞÑÙáÖÙÕÒÙØÚÔÙèÞÏÜÛÖÚåÞØÔäáÛÛÖâÏÖÖÞß×àÔÐäÞÚ×à×ÖàØÙáÜØàÕØÝÒÞÕÞØÜÙÐáÑÜáÑ×ßÖØÑÚäàÜÖÕÖÞÝÓÝÔàÆâÔÓÏÞÌÐÛ×ÕÛØÙØÙ×ÙÈËÏ×ÚÝÖÜÙ×ÐÔÖÞßÔâÝÑÎÔáäÛÛÜÙÓáÎàæÍÏãÚÜàÔÔâáÖÖØçÖÊÏÓÝÞÖàÑÝÔÓÏÔÔÐÚÓÔßÖÙÙÓàÕÝØÒÔßÍÑÝÒ×ÙØÛØÍÕÞÜÝÒâÝÔÔãàáØÕÛÏÌâÞÔÏÚÎÛÚÏ×ÖßåÛÉßäÜÇÝáÏçØÕàÕÙÑÔÙßãÑÕÜÞÔ×àÛàëÕÜØåäÔÙרÛßßÖÔ×ãÚÛÝßâÐæÙâÊÔÓáÜÞ×ÑÛÓÙÑÙØÒÕÝÙËÖÝêÝÖïÚàÒÒÞÔÕÙÙÝäÐÙÔ×çÔÔÖåÒØáÑÙßÖÑÖÖÔáãØÓÕÔÕÙÓØÙç×ÌÛìÖâßÞÚÜÏÛÛçÐÕÛÊÓÓ×ÕØÛÒÙØ×Ü×ÔÛÝÛÑÓËÞáÎáØÙÛÖÍãÚ×ÚÞÕÈÚÕÞäÕÜáàÚÔäàÔÜÜÎÛÔÏØÍÕßßáÚÕÏÕÌâÈÚÚàÙáÓÃÖÙ×ÔâÞÓߨÖàÓÒàáÜÕÛÜËÐÏÕÓ×ßÊÕåØÑ׿àÚÔÖØáÚØÞÑßÛÞÍÍÎÔãßÔßé×ÕÙÎÉÝØßââÒØÚÖã×ÔàÒ×ÌË×ÚÜÐÏÑÄÓÆÜÛÝÑßÙÚãÚÕÙÚáÑÝçâÖÝÜÙçÌìÓÛÏÚÑÕÔÞÏÖÝÔËÖÐÔÛÕÔÊÍÓØÑÔàåÛÛÖâÔÕÞÞÐÐÏÜØ×ÕÓ×ÚÞÔÚãØÐåÜÛÞâÔÞ×ÞØØÕ×ÕÝåÔÔÜÖÙæÞÞÐßÓÙÚÙÊÜÕÛÎÚÚÞÏÙÎÛÝÚàÏ×Ù×ÝÖÞàÞ×ÛÚØÞÚäÐÛØÚÚÛàâÑäÚÛ×áØÒÕÞÖÏÚÕÍ×ÓÄÛÏÓÜçàÛÓÝÕÛìÔÓÔÜÎÞ×ÚÔßËáäÏéßâÝÒËæâÞÚâÙÜãÑ×Ô×ËÕÓØÎÖÑרÛÎÏÓÛÓÚÝäãÓÙÝØÓÖÌ×ÓÍÚìàÖÛÙèåÏÜÖØÌÚÚÚÝÚØÑÌ×ÕÜ×ÜÝßËÞàÔÓÞÚÔߨ×ÝÖÛØà×ãÞÛÛàÉ×ãÜÚÜ×ÕéâÞÛÞ×ÛàßÞèÝÝàÓ×à×ÝÜÙèòÖö‚÷î÷âØÙÛØÓÚÙ×ÙÊÊàÞãÊÜÄÎäØÓÙÌÖßÓÞÔÒàÏÜÍÝÛÞÖáßáØÞÕÚÔÓÜåÛâ×ÔØÒÝÙÞÖÔ×äÛËߨܨÞíàÊÜÙÔÞ×ÕÙÝÒÛÙÓÙÝÜÚéÏÈÝÓÒÙÔÖÕÎÛ×ÕÙÎÖãÝæþ‚÷áíäÝÕĶľ¾Ã´»Ã¸¾¶º¼·¼À¿¹¸¹Ä¾»¿ÃÀ·»À¸³ºµ¹¾Âºµ±¶¶Ã»¹¶¹¹´¶·¶Å··¾¶½¿ÆÃÀÁ¸¹»º½¶¹´º¸®±¸·´·¹±¶¹»ºµ±º´·¸¶Âº¸°³¹½ÌÐÊÊÔÕÒÍÖÌÙØÔÑÊÓÚÔÔÎËËÙÓÐÖÒÓÕÛÞÒæÙØêÏÜËÖ××ÚÑÒÎÍÓÏÒÊÚÜÛËÖÏÓÕÐÒÓÍÌãÑÒÌÓßÒÞÚÏÓÑÐáÆÖÎÜÌØÔÐÍÒÚ×ÔÖÔáÍÓÚÚÏ×ÝÒÎÙÑØÏÐÛÑâÓÒÉÍÑÔÙ×ßÝÏÓÊÜæÖÞÏÓÜØÔÛÛÕÝÒÜÑÔÛÙÖÖÎÙÙ×ÚÒÑÓæ×ÔÖÐÔÌÛÝÞÎÜÈÙÚÓÍÖßÖÕØÕÖÞËÓÓÒÔ×ÓÛÚÓÊÑÍÉÉÔßãÊÝâÖàØÛÖÜÔßÔßÖÔÔ×ÉÖÙÔÌÜÐÏÔßÝ×ÕÑÕÖÕÔÏÔØÑÖÏÓÒÖÖÊÐÅÕÐÒ×ÔÒÕçÝßÑÜÚàÏÊÔÌÜÇÉÎÎÓÙÝÏÕÒØ××ÏÚÜÏÚרÑÏÒÙËÐÓÓÒâÝÎâÔÄØÒÔÚÑËÕÏÐÚÍÖÛÚáØØÔÓÍ×ÏÏËØÆÐÔÛÒÕÖÌÓÚÊÐÏÎÓרËÞÕÜÔÍÙàÛß××ÍÕÏÅÍÒÏÙáØÔÕâÙÌÜÓÓÕáÒÐÕßÐÒØÎ×ÒÒ×ÚÚèÌÜÞÞ×ÕÊÑÒØÛÏÜÁÕØßÎØÉÙÔãÓÝÑÌ×ÔËËË×ÏÔÕÏÆÝÄÓÏØÌ×Õâ×ÙÔÎÔÜÍåáÜÜÎ×ÖßÖÙÊÏÎÐÒØ×ØÕÙÝÖÕÐ×ÌÓÖÎßÐÖÕÔËÙÛÝÝÝÅÌÎÊÖÖÚÖÛÉÛÝËÙÔäÐÐÓäÐÚÒÖ×ÔØÉ×éÖØØÉÓË×ÔÝ×ãÙßÖèÐÙÑÓÖÕÔÚ××ÖÞäÉÎÒØÑÌ×ÎרÐÏÏÜÕÙÒÛÕÙÕÌáÕØÐÏÒÛÝÙÖâÚÕÍÓÉÓ××ÍÔØÖÑÛÒÏÎÞÖãÙÞàÛÑÒÐÌÚÍÚÓÓëØÕÝäÍÎÌØÓÖÚÜÎàÖÑÙÐÎ×ÓÚÔÎßâÉÇØÑÝÜØÏÑÍ×ÏÙÌÑÎÎÓÝßÚÛà×ÑÏÓÏÕÔÊÝÖÌÕ×ÚÒÕÞÓÓÐר×ÔÞÒÖÞÐÖÕÌÔÛÒÏ×ÕâÜÒÞØÛÛÑÒÓÑØÜÔÜÝ×ÏÔÉàÖÔãÍàÔÜÔÏÎÛÕßÐÚÔàØ×ÏרÓÕáØËâÀÕØéÌÓáßרÐ×ÙÐÑÔÖÒÍÖÏèÕÙØÑÎÓÌØÏÑÏÕÑÝ×ÝÚÖâÜÓÜÓáÐÞÖÎÓÕãÛÛÑ××ÌÖÖÒØÓÎÑÝÓÑÓÖØÖÌÐ×ÓÔÝÕÕÙÛÙÑÛÌÒÍØÏÝÊØÝÛÌØÖÖÛÜâÕØÄØÒÜÚàÕËÓÓÏÖÕÚÙÓÓÑÝÖØèÜáÞÙÕØÈÌØÍ×ÛÊßÔÐÖÚÍÖÕáßÍÛÏÙÛÞÔÎÇÔ×ÎÑÑÅÖÒÔÚÞÔÙÑä×ÔØËÒÕÓÝÐÎÐãÚÔ×ÖÐÖÓàØØÐÐÕ×ÛÚÐÓÏáÖÜÖÛÒØäÛÒÞÛÎâÕâÎÖËËÍæÖØÇÔÍÕÙÐÎÒÐÐÔÔßÖÕÓ×ÍÜ×ÒÓÛÓÜÖÔÒÏÉÝÜÅÛ×ÚÓËßËØÓÒÍÑÙÓÖÉÑÚàÚÙÐÕÔÔ×ÔÎ×ÌÏ×Ì×ÑÊØÚÖÕÝÑÒÛÏÙÙÑ××àÚØÙÐÈßÕÙçð‚ ìÐÐßÌØÒÛÓÕÎÖ×ÏÐ×ÔÕÎÒÜÉÖÒÖâÙ×ßÔÖÖ×ÚÖ×ÉØÔÙÑÔãÓÇÜÓÙÔÙàÜÚÚÛÍÔÞÛØÑÖÐÐØÕ×ÜÜÕÚÉÏÓÖÛÚÓÊÝÌÙÒÔÑÖµ²¹®º²µ²µÀ¸´º¿²µ´»¶¸¼³´·º·´¹º·¹Á·¹º¸¿±²µ±µ°¹»­Ã´¸¹º¸¾¼ºµµ®µ¼º¹µº´¿ºµµ¼ºÉÔÏÂÎÐËÎÎÆÊÈÌÉÈËÒÍÐÇÐÑÆÍÎÊÍàìåèãëêèêîàæàæèåíéöñìêéâïññÞõáïæíêìëåóøáç÷éñõáçììèéòüçëçëñíìíúåèêäßñåäìáëÜóèÜðíâáóíòçåáñáçãìíçãïÛÙåÜëãìéíßóââñöâßÜîìéñâçèìëõõåáåáõÝñíèßëçíããéâðîõéñðøôÝïïíêêæ÷éæíé÷êàèáñéçéâèôëæäçëàæêôæóèïíêêàóñëôòåâîóçùù÷Õðçðî÷êéòïàëêëëõçôæìäïàâòãñæääùéëðéæçîçåðéñèäëêììææòíòßÝðÝêõÛÛêèêïßäðìæÝãñêòðáäùêõíêçíôçõîÞêåèíçêóïíëáêåîúîîëôàäåçéçìãëêççêèïíéçíãããïæëóìåõõõàßæèíÜåàéòðìäîèøöëäêêñíùíìâðïðæìôêßãòäðëãïîôãâêîàêóùÙìéêúßìøæòèòðñãîáîÜäâðéäâéîëäãíéæìÜåèéùÝòÝéìàîèåíãã÷ãäññäáùåæõßíèëðééÝòêèÙèý÷áñçíäéæçðåÝæàøæðéàáåóçóñÞáæåçüíôïõãîîõíäôçèîãããáêáñãóéëçìîäïåãæìéææáèëóêãóãæêéßåèÛèßêãðäêâÞìîæñæöçôìñîñÞïïíêíìïâðìæàèëìòðóåèåñîïêåêéèéíæçíåèÞéëéëííøÞèþìîûøðçéèßãëãêëÚëèëèôåëóìñîíãïýóïàçéñéééãêäûäèâúäíïèÙßíççèêëãîëíèéÛäòãåèçåïíïèíèöåâíííãêìïëíêñ÷àçîãêðéÜðæçíãæãéîÜÞãäÛãáàêîìãëÝëÝææôëûæïîããßàñìïçòèëñëåõßéññèôëÜæÜåêñãçÞäóïîòÝêêõØéðó×ïèêèìçåðèíïäïçîèáäêßõêòæáõÖæãåéåóàåëêâÝïòõïééìêîïêáçâàñîåëéêèâÖðèÙéïïâå÷äïæâñëèäêóéãæææççñíããéâåèèßëìæìíæÝíðâêîðëðîùïåíÙöòáäâãæäâñâàéóïÞéïêáïïåèåáçééîæåëëè÷ääõäåçãïìáõéóäçéãåìäêéçíøõëíááææâÝáàÞïãçôãïîíïóìèáéòñáèæàëèîìøáùéòçñêêÜØéìñçøìïáöêáîùòãêúüîèæáðáøëëâóçõíòèìãçîîèéëéßæÚòèîëæáèßàéåêäæíééñòéîóîåäçêæððæòææìÞíôÜóæáíåééçíóöõðëøêèòúåàóìæêïßìòòôñëîËÅÈÍËÊÀÒÄÃÍÅÏÏËÊÔÌÉÊÏÎËÉÆÆÍÌÒÍÊÇÓÕÈÏÃÌÒÏÓËÆÊÏÆÌ¿ÌÄÇÆÒËÌÆÏÍÈÊËÏÆÂÌÃÉÂÈÑÉÏÈÍÍÈËÎÏÌÑÌÛÔÏÏÈÐÓÑÎÑÊÊÇÌÊÐðëóëã÷äûçíéõåíéíìñçÙàèò÷éîêëöñÞõñõóôëäïæîîéñëôëíúóñôåëèäíèæëùëìßóòèèòíõôèîçåâêòðëòöøêþâúë÷êñÝðçõãîíïîþéô‚òæäèãêçïêñõóãñÞñååîòæõñöðôïúíþèæïæíéíêéðéðäéøòãäìòåêæéëïðíéûì‚ó‚sðåìôäëññäñíîíñæö÷òòåàòëëôîòèöíäÝáòìõ÷èðîïííèêíêãóñóìøîñîôõäçöðììéûúñëñéïððìóùñýâûöëðäóòïöóããßîåæòéðíïñöæïêòëåèíëòóêèìðîèéúöâïåôïåèôñèöðëçñæíìòäåèêõêòå÷çíýèðòòçíè÷öóåóåöáîñëöçðóéíóôçïêìóõîîôäìòðïôéôíìïîïèïðíèâìæñãìêéëéííåìõûâóìñìïìõðôóíîöáæ÷óúóßäóûçîîûäÞíèöìèéíëñêìíìèëâäìõèíïñêêèâïåêãññ‚èßòùëâçíìôèîùóîðèøúïæéöíñîêïôçêëèîìæçáãóêðïçäîìóõ÷ïïïïñõïóëñìòèðíùóúóæöóçîãæðêùçúîøâüéíæôñïæ÷ëëêø÷èëñöðáìùúåëôöçóäëõóíéëéøèôæøìéþåüëæîîêìçîñäëåðíæñðýíïäãñîéêîäñìöèøïîôð÷æïøôçÿñôõëîîóúêéñíóíöñóîúèóñâéóíéíâææöîðíèóøãÿçèõðëñðæîâéëéèíïæóéïéäöéóäåîóñúòîéêðîììëòæßèääöáíííðâêéïæç÷óûäé÷øâòæñïí÷ïòßùëóâæéñóý÷ùñêæüñíéîøñéïöçùêìïìïéôñíäìçðéóóñóøéçê÷õåêüéêêïììæìæêëíúóæòîçðéðóôêòîòæîèôáïåõê‚ìúìçæ÷êäî÷óíî‚ñëãôôóò÷îàíôéèúäæáëêóãôêíéëèåñèøíäöòêóíëëåòôíòëðøõîééçèçâëçÞéîãêíòéôçìóæíîùàìðëñîìæîïïêïèëêï÷óûìòíñíðóðïÞóåøÝ‚íûçðêãîïâôçõñðëóçæíòòéïûêåëûëóîíîìïòßíõñïèïëêííèñæêêèçòîíñìñèòøéáóåéøçõéäêòåìéäñð‚êâæ÷èîïöõëäéêóæèéîðøæþîæìëìöãðôçèâæðñàëøðæñîðõñî÷íõèîèïíúâòëîÓòëîõæïìóë÷óóáëíèâæóòîîåîøôóæõïÚèéçèééäïæøèëñïóçóÒÎÖÆÊÔÏÎÓÅÍÇÏÈÑÐÔÒÐÔÓÕÏÌÍËÉÈÔÏÒÑËÖÒÕÈÑÍËÓÐÊÓÒÓÌËÎÐÚÑËÖÖÒÏÓÒÕÒÎÔÕÔÉÍÎ×ÊѶ»²¼¯¹·À½½´²·´½·¸µ·¸¹ºµº¸¹³àÙáÝ×ßßÜÞÞØßÖÝÌÝÜÐÖÒÖ×ÍÑÙáäÙÞÕ×ÈØßÕãÕÓÍÐÞ×ÜÝÔÍÐÐÜâÛÌßàÛÌáÕßÙØÌÓãæÕàÜÓáÛÑÇáÛÕÕÈÒÌÒÙÕÊÚØÔÕÝÙÞÝâáÝÚÞÆÜÚÙÓßÚÙÑäÕÜÍÒÝÙÚÓÔßÒÞÜÔâÚàÕÜרâÖäÔÙËÝÙÔÕÏÑÝßÙÜÏ×ÔÕÙÚÎÚÐÌÓàÜÜÞÝØà×ÜËÔÝÔÙ×ÓÑÖÚÖßÚÖÕâØÜÍÛÒÛÖÛÖÚÞÖÛÊÝÕÕáÜÓÙÕÔÓÞÓÙÖÙæàÍåÅàÛ×ßÌãÛÐ×ÕçæÖÐ×ÙËÛÓÚÕÞÒÙÙÖåáÞÓåãßÙØØÐÒÞÕÛáãÕÕÝâØÚÜÛÜÖÙ×ÙÕÒ×ÝÝÙÜÚàåÍÜÖÞÍÜÔÖÔÔÓÞÒÛáÖ×âÙÚçÛØ××àÙÛÎÖáÔ×àÑÌÓÓæÌÜÏÝØÔÍáØáÔØÙÐÔÙÝáÒÜÙâÞÐÖÝêÕÛ×ÕÞÝÚÚØÛáÙÞÒáÔÏØÚæÝ×ðÒÒÐÛÜÞÏÚÝçÔ×ÐÜØÐÖÑÛêÒÝÓÝÒßã×ÔØØÞØÜ×ÚÑÇÞÑÙØÍæåÝÐÓÛØØÖÞÜØÓáÕÖÛÉÍáÖØàêäÕÖçâÙÝÑÚÞØèÞäÛÒÛÍØÔÜàâÕÜæÚÑáÜàäÑÜ×ÙÚÞÛàÙÕÚÖàÛÕ×ÚÙÔÕÖÝÏÜÏÒèÖßÞÕÐÔçâáëÕÖÕÑãÊÖÓ×ÊÏÍÏÚØÕÍÑãÛÝÛÚÝâÖÐÛÑÓÊÓÔÌÔæÛÞ×ÔÐÛà×ÏÝÙÕÖ×áÔÞÖÍ×ÔÛââÓÛÝÔÜáâØÝÏßÕØÒÐÈßèÌÎÛÖÍ×áÝÙÚâÈàÒÝÐÖÐÚ×ÑÐáÝÔ×ÓÔÎÜÍâ׿ÕÔÛËÛÐÙËßÎÙáÜÞÙÚÞ×ßÜÎÚØëÜáÒ×ÜÒßÛÚÓÚÛÒß×ß×ÐÐ×äÙäÎØÖÚÙâÞØÞÙÚØÑÔÎÔãÏØÑÕÒÖËÐÆÓÛåÎÞåÛÝÑÞÕÚÙØÐÒÙ×ÕÝÙÞÑëÛÞÛÜâÔÓßÖÜÕÏåÏØÓÒÒÏÒÙÞÝäÝÚáÑÓÚËÔÓÝÓÎÑÙÝÑÛÎÚÔÞÕÐÙâÖäÞÒÚàÔÕÎâÚáÛÙÞØÝÝÒÝÛØÔáÛâÏë×Õ׿ÌÓçÊÛßáÛØÝÛ×ÎÕÊáÚçÜÞÔÐèÈÝ×ÕÐàÞÜÔßÚÓÙÚÛ×Û×ÍÚÖàÑâÜÍÚÜÓÔÚÒÏÓÕÖÚÔåÓá×ÑÓÕØÎØÖÓàÙÚÛØÓÏÖÝÜß×ÙÙÜÙèÙÝçßÔÐß×Û‚EÚæÈß×ÏÇÒØÝÌáÙäÔÞÏÏÜÛÜßÛáÜØ×ÛÜáÍÕÙÛÚáÕàÒÕàãØÛÑáÓÏØÐÛÏÝßÝØÙÝÖ×ÝÞãÜßÞÖÉÓâáÊÑÔÕßáÕÙÖáãÏâÜÒàÐàßàÉàÎÐÏÎÖÕÙØØâÐÕ×ÓãÞáãÑØÒÖÒÔØÛäÝÑÚâÑÜËÑÛÔëÖÙÝÕÚÍÑ×ÎÐÈÚßÔÐÔÕêÙ×ÖØÛÕÖÚÚÛàÓÚäÚÏÍÏÓÏÚÒÒÓÝÜßÕáÔ×ßÞßÜÔÙØÜØØàâàÓÛáãÕ×ÛØëØÑߨÚ×àÒÝäÖØÖÐÜÙÐæã×ÖÝÖÐãàÐàÞ×Ò×ÛÚÜØâÒÏáÕÖÎÛÑÓÕÖ×ÞÝÙÖàÕµ¾·»º¸¸¶ºÃ¼¿¿º¶¹¼¼µ¼»À¿¶À½·ºÃ¿¶·Á¸¾¾Â¿À·»º¸»¿´ÁÀ¶¸¹Âý¼Â»º¾¼¸»··»º¶Â½³¼ÊÅÂÆÀÌÈÈÀÉÅ¿ÃÆÅ¿ÐÈÄÄÀÆÆÉÇÉ»çåØåÛÞèëÝ×âÚëèââëáàäçêìØæðáÛíçåïçÝÝéñàÚáääñãå×ãìæãäØßÒßÝ×Ü×îæßåâÝïãàØæÛäéáåÜàòÞÞÝÚééÝåòúÚìîì×Üâß××ÞåëÜàéçÛàåìèÞâÙÝçèÜèàããÜâãççãÙÝââñÝìÚêÚçèæïèìäâãåÞÝäãßÞëðéÜáÖáßéçä×ÞôßããçðßáçæàÔèåÛåèçïÕæÝÜÞçÜíæáåäæÛÝãéÚìØéïáëߨãÜÓÞåÜÖëààÙëäáÛêÙâäéççÝäëëçâÞñÜÝÝáèâüÜÞÙØèèÛáæäáÞéãäßîäßêåêÞçàæÜàèâéßáäêñÝñìçÜîåßääàóíÝãßÝîéÙìÞÞêßÙÜÕßåçãÜâãåèâÚçìëÙïèÜåâ×ñæäãàãæÜÖóßàÚÖåÖîÕêââßÞßèêÜéáÙòììåèèàðçäãåÛÚãÍÝÖÜëåÜáéöãÛã×âäÜåçåìæäÙêäæåãåâïÚâßàäèÓëäêäââççììäÞíåàÛôéåÖâÛèìêÙëÙàáßæÜÛìÝÜçßéÙÞâéÓãêâàæïîàÜèíéÝÕîïããßåãâÝÜíèáÝèæãßÝëèëíãØãÞäíáÚàêßëçáçØÝÞßçéÜßÚèìäêëæÜáãääÞßÞÚçéçãÜæÜàèáàæàâÔæéåéßæÙéÛãäÜÑäÞßãñðæÞßâåêïâåéëæíìâÜãçÜñàõäçêæòêçâãåÜàãêâæáÚàò×ëãä×ãæéãÖëâæâÚìâàæçÛèèäßîãÞàÜÙáãÝäåÛåçÛôÙñêäÞéàåâààßßåÕæÞèÝâáàèêÞÛÝåãØãäÛØØááðæ×ßÞãëÕçåàãêÛçí×ïîÝáØê×ÚÛããëàðàÚØêØâëåçÛãêèð÷íÝåççäéÝÜçáéîßëåäàãìæãÙßëßÞ×èßìèÐÜãÛáÝÛåÜâææØàâÞãëåàâáÞãÞåäàááæÜåàéëêåæãÚñäæåßÜÚäæ×ß×àåÕßðæåÛààìåãÝçãâãäåãâçÛæçÞÜÕßáÜ×åèêã×ß×ÖåßìàóéåÙàÝöÝáÝðÞåäæàãéâáäìñáêáèìèëäÜÞÜÙçßããìåÜÛáçàâáßßåÛæàãÝìÛàçßÚÖæèããàÛØèäÜßåáðæêáÞáàñÞàãáêÖäââ×ÙâãçßåãæîßÞçÝáâéÓêÛÔÝíÜÞßÞàÙäáßÕßïçíÛßá×åìàåíáêãïáçæÝéìÙÝéÙêáðåÝêÞÞãñÜàÚâìàëëåÓïéà×îâìäàÜâÙÜæâáÜæáããåâØñëáÒêÝáßãÞÖÞæèßáåëáãÞãÛäßååàÞÝäèéæéÙàæÕÝèÖÙæíáêãìßÞÜáäëðáàßíðå×ãáêíÛÜäæãÝìäàëãéàèàéäßèà¿ÁÈÊÈË·ÇÂÆÁÆÁÂÏÅÄÃÁÇÄÆÂÉËÈÅÈÈÅÿÀÂŽÃÈÇÁÂÁÎÇÁ¾ÆÇÉÊÉÂÉËÅÊÃÁÃÂÄÇÂÈÂÆÊÊÃÊÄ»¾ÅÇÇÄÄÂÌÁ½Ç¾ÆÃÉÀÃÃÅÂÂÄÂÀ¾¼áÙÞÛÝæÛÜÞáÙæÜæÙÚâÙáäÜãÙæâäãáØáÓæãöÑÞÞÔáÜæáÞÍáäÜâØÝÝäÜÜÜæÝÞßà×ÜâäâéÜâéÙÖâ×àØÝæââêÙØëØáÞëáÚßçßçàäñäåÕÞãà×íéêÜÜÝÏéÝæÛÜÞÖåçãÝìæÛØäêèÓãÖÚÜèåñàÝçáàÝãÙâßÒáãßæàéßâàâäÜàÜèâÛíÚåÜÚâéàÝÚß×çåçÕæÙÝà×è×ëäçäÕåÞߨäØÛæÛÚÜàéÛâÞßÜâÚäßÞàæäæáâÝßäÖåÜê×ßÒÝ÷üÜìßÍåÚäàæçÝàÚÙÓÖçßàÛÝêÚáåÝØÖÚãáæÝäÞåéáÝÛÝàáâæØáéìçßÞêäÞäââæäÔéÛåÖéìÝäÖÛìèå×ßêëÞëèÕàÙòÕàÝßáàåàêãÜãïàÞäÝâÛîáßäâáßêáæáîãïâßÜàâçàäàÜëáëÞãÕíßãêåàâäéæÚØëÚÛêÝîÛðßäÙÙåÖÞêìéÞâáçêÛØÕßòÜØÞ×ÖàåäçåâåäÚäßâêÝÚééçßãêåâÜâãáÞàÙîÞêÞìÝáÕßáäÞÝéØßáçØÜâßåäØÛçåØ×ÞæÙáÝìðØèêëßäßèßëÓÝêÙÞçÕâðÞãâÎÕãÛåãÖÏ×çÚàìëåßðâèäàäìÞÔàÚéòïÓâçÔÓàÜãßãâêàåß×ÕæØäÙæàØÞÜéèæêàÕæâÖßâÝÚéåÙàÝàÓÖäâéäéÝçåï‚àÔÕâÞâãÜÞÛîàßåñÜàÒàÜߨãæáòëçßñßáæáÚÝÖêèÕÝãßÜÚàßéÝéÜÚàØÚéÞäáÞäààéãç×ÓßÝÚÜߨÜáàäåÞÕ×ÝæåðäÙÙßéÙéæååêââáåÞäÛßäÕäÔéÝßãóÚçãÜààãèåÛÓçèççãááÝÛæàÛÖÍäáåæáÞêàäáààäáßרâáåÛëáÙÙÛãÛçæäáíÙåØÔØßÝØÜÒÙÝâßçÙÝìåÞããÞÛäôæðûäßäãìäéÔïçØæÑèàäïãÜåßï×ååÙæÙÖçåÙáãæßçÝáÜàáÛéääàÜÝÝÚçÛáàÝÚãäÞãçßÙèãåæßâàÖèäÝïÚÛâÛãßáÔîêãäæåáåãææßáÛäãÛàÙåÝåæÞÛìæåÛØêÙçØßéäßãáéãçØêççÞßäâéìãêÙæäáèâßáéßÐÝèíáØÙÝïáõèÜäßßÝêæçéòáçêÏÝÜíëÚÞáèéÞéåáàåÙÚæÑéßßáÞÛèæëââèÞãâßãÛâÛåÛØæèÜÜÜÜßâéÜååâÕÔãäßåÞÛäæßãßååàÞàçÓáàèÞâÖòáÚÕÝäßÞÔßßàôßÜÜãØèææßßÛÚáëÜïçÜåàäâåØÚäÙÙØÝÞàÚÕÛåØÑÙåÝêßàãæéÒèÛâáÞåëëçÞâÕàçÝäåêèßÕâåàØäàãÜØàëÛêåçëÞáêâó‚ìÝÇ¿ÀÄËüÁÄÀÉÄÆ¼ÂÅÅȹÊÐÀ¼ÆÁÈÁÍÀÀÀÄÁÆÑƽÄÃÃÁÁȽ»ÀÆËÆÇÃÃÂÂÃÁÁÄÉÅ¿¿ÌɼįÇÄËÈÇÌÂÊÅÌË¿ÎÇÊÉÂÈÇËÈÆÆÑÉÒÐÄÏðêðãëæëæáääãçðïñæìçäðÝïîèôãóíèææäáìßÙòçìõöèàêåôòÜíåëèæíâóíèìðáñèçêæöêçáãæããìØæáâïèëçñçâÞûÛèâÞÜêáÝ×ßíãðóòæØÞéäòêèâÝðéëûêëíãêóäåíèêðãØêëæääóéîááâôèíäæîèæâäÞàåâçèêçæåàááßéíçæäïÝ÷ñäðâáéöéÝîîåáàíïçãíååñáúåòìëíôìîöæàèéãåëîáÛïçðëäóàéèôîçäõïíàíñãæàâéãäççãéõëìêðêôéæíïëîíàïëôìôíÜîÞëçíåõâèëæìæèâæÛÕßüêìêöæáîêêÖëãëßñåðÝíìÜèòõáèëãäæçìóíåáÜëêáÙêíÝèîóàèñýéÞííçäáèýêòîáóàáÞàäãèíßèìöèãéëïÛêßîäééæåñïäáéæíêÞäíèéáïçðæãïêãïïÝêåèóôäèêìèìîéÞíçëåëéâò÷æìëëìèëÝêîèîæìèàáàìßéìíìÞëáéîúõïçñõîòîãòãåßÞææéíëâáçìàíåìíâèòÛèëëëêðÞóúíÛÕéìíóãéëö×èêòàêæåñèðíòòõäïãñóéçæñãóêñçßÜáÞîéèèééæòöðèßïôìÛçíéôÕöóãëèøéíâãðìïôèçåØðôïéìõìçìí÷áïêçåæãåìóñíôååÝéÞÝðçîÛàåáñõïÞéæäâäãâáìíìëìäãùæÞæìëîæñìäÝâáëåÕÜçæêíëóåâêæåÚóéëßîêåéãìáæîôßãìãëèúâêåíåãçíðêâåïñâÙêÜàâðãâßíÙõãèæÛÚëáìåìãëîçÖâðíñêÙãÚåêçÙêöñíêæÞþåìäêïõåæçûåàêéèêâîâæòòîìèâõêâêëæçßßæäæçááøëÞîêçêÛëçéóíòòìâÞèÞæéâÿêïáåõîøèïéíâÙêìèçåí‚€ïãæåâàëéëëìïðåëåíîâñíèðâæÞßéç×éîèêïéâïßîÛãîäê÷ïêêâçêëõàïìñßáèææðÛáäéìåàóèÝäåäìîñçíÝíêèåæìñîëñëôäæåõéòâé×éêí×ÚäÞæëðæãòóíÝïäëåðâêàíùàçëäâìäîõêùòïã‚ãîòäòõÿñðò‚ï‚‚‚%‚E‚0‚‚‚,‚*‚)‚*‚‚*‚‚‚%‚1‚(‚‚‚‚ ‚‚ÿ‚ú‚‚‚‚ ù‚ñú‚þøðóïë÷áùíèêæïÞççôÞæñíèÞñÝíòãîìÖëííëæçäæëçíééãøìëâçìëÚâÛäíëçÜâìäñêèèáñîòïóìçóæóâÝæàôæåäðâãáüíåõïõÛöóâðíçßêçìãåëàèèäðßéãôéçÑÆÌÉÏÉÆÊËÓÎÅÊÔÆËÉÉØÎÒÉÍÉ¿ÏËÊÆËÄÐÀÊÉËËÇÈËÊÉÍÊÉÌÌÑÎÉÎÅÂÄÄÓËÇ¿ÍÎÑÃÅÐÍÌÃÆÎÀÂúżÁ·À¶º¿ÀºÁÁµº¶»ÁÆ¿À±ÓæàÜÖÚ×ââÍÕâÚÚÕåÖÒæÔÙÚÐØßÍÛØáÙãÚÚäØÔרÜèÓßäØßÍâ×ÝÖåÙÚÚÙÚÚÙæÜÚ×ÞÚØßÑâÓØÜØá×ÓÝáÙÞØÜÓáÒãÚÞêÔØßáÐÔÑÝÝÛáæÚÕÝàÛÜÚÔÎåÏÑÑÒÙãÝßÞÞáÈßææÞÖæàãÓÞÚÜâÝ×ÓÜÛ×ÚâíÝÜéÓÐááÜáÖÌ×ÕÙÒßáÔÕÜÙÔáÝèÖØÝÝõâÏ×ÛÛ×à×ßÜá×ÚÞêÐÛâÞÐÕÙËÞÒ×ÙèàÒßðÖߨàÐÔÝáÔÚר×ÙÛÕäÖÕÝÞÖÚãÖáèÄÞØÖÌØÒÙâÕÞÕÚÕàáàÖäÕØÞçäÔãØÒÜãÕÝÞÔäÚÞã×ÜÔäÒÔËãæáÛÕ×çÞßßÛÝÑäÌÜèãÑØÞáÔÙÞÏßÏäåÞÕÙÒÞÔÛàÛÓÞàÍØÞèÔàÕßÔÖÖÜÞàÒèÙØÚÑàåÛÝîÝóÕÛÕéØÚéáàÝÖÚËÙÕÞãâêÍÚæØÏÝÞèéÚßàÚÒÜáÝâ‚?ßÖÏÓØáÝÕÞÜØÔàÐÝàÑÏßÚàÜâÜÝÖÕÚÕÙêÛÚÓÎÞ×ÙåÝÝâߨ×ÜÝÛäØÙâÚÒâØÔßÎÕâßæåßáÜß×ÝÙáÕèàÒÝ×áÝÚÓèãÑÌßáëßÓ×ÝÖàáåÝÞ×àÌÚÜÓÞØáÔÖÚàØÛÑÖáÓçãÛßÜÛ×ÙÓáçÏÓÐÙÜ׿ÜÙÙãÞÝÛàãáÕÞÓ×ÔåâÞÌÚÖâà׿áââ×àÕÙÖÖÜÑÜèÛÔÕÞäÞߨØÝÓÕãÝäÍÞÕ×Ü×ÛÛÒÓÐàÜ×ÑÖáÛÚ×ÕÚãÏãàæØæÑÜàéÙÏÕâåÓèØÖïÛÛÝÙßßÕÐÙØÙÜÍÕæÙÞàåÙâÝêÛÖÍÚâÜÙ×ÕÙרÓÓÜÜãÞâÓߨñÚÔÒàÝÚÕÛÜçáçåéÓÕÔÛÝáÚÚÏÜàÕÛÛÙ×ÜÝ×ÕßÔÓÜÕââßâÓßßãÝÖ×ÝâÊçá×ãÚÞÙæË×ÛÜàà×ÔæåרààÐÚØÝçÖâÚÔáÓØÙרâÚÝßßÝÓ×ÜÐæßáâàÎåÍÖÇßÙã×ÕåÏáÔÔÝÜÓÚÓÞÌÕáØÝâÙÍÚÙÚÑÓäÜØÞÜÚÚÕ×åàÜ×ÓÜÜÛÝÑÞÙÜ×ߨÚÈÜàæÚÚÓÝßãâàåÖÚÏÓÜåÝÙÓèàÎÚ×ÙÕàâÖâÐæîÓàÜÑÞÛÙÕÐÛàÖØÛÛÖÚßߨÞÚàÔÛÞÛÏÛ×ÕÙÏÚÏèÜØÛßÓèáÕØíÛÜÕÙÙÚÖÖãäÖÕàÕêåרâÙÓâÎÖÒÙÕäÓÑÙÚàÕÚ×ÜâØÙÙÛßÜÙßÞßàÔÜÜÍÜÛßßÜâ××ÜÔÙÔèÚÜßÜ×ÜÜÜÓÜßÚÜÓÝØÖáÏåÝÕáÚáÞàÕÑÒßÛäßàËÖñßÞÙÞÙ×ÚÛÖÚÖàÔæßÛØåÚÞÛãæÞÞßÞÞÞÐÙÝääÝßÔÖÜÝÔÛÞÍÙÛÔãÏÚèÚÑÕÐÙÏãÙÜÖÝÕÙÙàäáåÕØÔ×ßåàÜáêãÝßÖãÑäÌÞÜÎÞÑÜÝÒæäíãàÕæÜÓåÜãÝØéßÞ×äÞÝââÚÜ×ÐÕÝíÛÓàÒÛÕéáàÌÞÙÚ½¿¾¾Àº»½»½ÀüÂÀ¿¹¼¸Ä»À¼ºµ¾º¸½Á¹º³¼»½¿Ã¾Å¾º¾ÃÀ¾Â»¼¼¼·¾¸¾¿À¼¹Ã¾Ã¾¼Åº½¾»¿ÀÁ»ÅÆÊ¾ÅÿÄÃÃÈÂÅÁĽ¾ÂÅÂÈÆÉÆâÜåëèÜçÛßÞîãÝêäÒÕØéëòàÛãàåìæëçæÜÙéáèØéßÝØÞâßêÛàëìØÕÝåØÜìèäâäÚê×åâæáÛæàßÞÜÞì×ãÝãÙíïçòåããÝéèáØèààãâçèçßàÝæÜÙïçÞ×äîâÚÙãçÖàÜæÚâáçâçâéèÝÔàáàÞ×ïââÝÛÝèìàîÙãßÜçßæÞçèââÙçéçààÛäßêáãàëââéîåäÜäâåâØ×ìÚèìÛÞëçàãäáâÛÞÝèÖÛáççØÝߨäëÛÝçåÝäáàááÚçÜêÞÞÝßÝèàÞâäÙØìÙßÚêåßìÞá×âëæÚÞêæêå×ßÑèãâÛæãââàáãÛãïéàêÚßæâ×íÜÚÛëçÙÙêâßáØëèãÔéãèëÚèåÜéêæåßÛÝáååÞÛåÝéàÞÙäãÜåÝáëÙââÞßîæßìãéäèÙèèáÙêìñáâëáèÝêÙâáâÛáçãäåàäêôÞçâÞÞáìâÙåßñäçäàãäâÜååôÜëáìáßÖÞÙÞæëãÛßÓßàâåÜìÝÕáâáëâÞßåÞÜâØÞÛÚæéÝåêæÚÞâàà×ääîîåÕÝæçÝÖäÙëàîÞÒâÙàÞâîåÚÚíéååÜãßßäØáÑßÞâæìÛÞãÞÛäëëçæõÛôçäÜñÞâèìåîÕæÙàÚÝÛÞé×íëÚÞéÕäçîëéÞæâÜâããßÛåáÝÝãóéù‚þäîÜçêåÞìÝÜøÝîçåÔÝéâëÚ×åéñÇÞÚäìçæãØäëÛéìáààãàääîèÙÛßçÛèëî×áÚÚàåãçéèêÛçèÛëèÝâÙØæÚØäçÜèéàçæâííßáâàèêÙÞãÞÙäØàØÚâççâáàáâàÕñçáäÞÓèÝéêéÛÞèåêçãÝïíåßçÝããæÖìèïßåáßãÙáôéÞìÝ×äÙäáåæÖâßÞâðçßßçä×âéãÙ××ßæÜéÚÝÜáÞâáãèßÛáÙéèßÛèíìâàÜäàáÜáàÞÝèçäèâêâÜÛîá×ãéãêáèàßàòëéçÛàßëèÞÞÛÚÝîæåÔäßìëáàúäéáøëÜßãÜÞãÜèãáòïãâååèèÛóÝÝéäÚåÐÝßâêäêêßãòáéÝäâØáÛêæç×ëåÞàÛååÝæßÛè×ÞåçãàäáÙêàÚäèæÔâîÚÞääæìäßãÝåçìÝääåÚàÞáæëäâñäè×ÙáâãÙáÙæÛåàèßÔߨ‚ìßáÜÝàÞãàâßèèàçßéàÛâãêßææàèáØòÞâÞÞïäáÝçãðäóäèßäãÛêàèæíìÝçäÞææçèÛåàáâçèóîý‚‚b‚è‚¥‚7‚ðôíäïéïçÞìàãØÚçæáçâÜååÜáÜßãóÜääááÜòàéÞÞåãáçÞåØâïëäåëäÚèáÛâàáÛÞäêèãìãîÖäÜåäêÚäéÜãæèæíæäÛèÚØáéàÞäãôéèàåöÝÝèççâàèêêéçÛëÜçÄɾÂÉÍÃǼÅÈÐÅÄÐÃÆÄÉ¾ÇÆ¿ÌÂĽÅ˾ÂÀ¿ÂÆÉľÁÁÅÅÆÁ¾ÍÃÈÁÁËÅÀ½Ê¿È¿ÈÊÁÃÀýÅÅź¿Ç¿¼É¿ÂÂÁÇÂÄþȿſ¿Â¸¿ÅÆÅĿŽãÜåæóæÚØèñâàââéÕäßãààëùäàéèÞÝÚßÛíåéäóØÛçÞØØãÝ×ÜàèÚßâäÚãÖæØàïäéëÙáÖÛÚÏðààÝåíÖØßäáäãàÜ×ãêäèãç×ÜÞã×ÞÙÞçÜÜäÜäÚߨçßÝãáÑÚåÕçßâãÜÞìÝå×îâØÛÞäãßÛßåÕãÚâàãßèçäáöçöåï×â×ååßßááïÝÖÔãÝÚØáèØÝÙìàÔóÖçßÐÛãèãØØÞæÚÛÝãàçÙ×äÙÙåÞäà×íã×ÞÜÞßßÙÜÙàãàåãÞáçÕíÖßÚÜááàÛáÜáÕéßÚÛÖéÞÖãàßÛáèèåäÜÒØÕÙÛØÖÚåÚÒÙçâÝççáÚâÙæÖÐãæåÝçëÜÏæãÝØßÕèÚïåÞÑÛâäÔÞâÛÞÙÕÞÚÛ×áÞÕÝÝåØàíàáéÚéÝÕâÜèëÜäæâÙßèÙÛÚÜÕëÜÒÔßÒáØáÎÓÝÚäÛÛáØÔãßàÞíîÝìÜâÔ×Öë×ËÓÖÝÚÖäÔÝäßÝÛéâåëÝÛáêàÍÜÙßãæßÕàÖæÝêãäâåÚÛÖåßÞââà×ààÛáâßëæàáãÐÚßÒÖãæÛÞÛÕåÜè×çîèáçÝåßéàãåæßÙÒàãØÛÞàäÑÛÛÛÙäàÒßÛÞæÛàßßäáàçèÜäáçÔÜÙÚ×âåääÛíîßÒÓÖàÜÒçäÜÎÚáãàßÚèÛÜÞåãÔåâÝÞåßÝâÛãØâÙááæäáÝÙÛàÚÞÜáÏÞØßááÛèÜÜßÝáàßàÞìÜäåÝßéáóèæÕØåÓßÚÞÞàïáÐéÛåÜÜçÙáÞæÜÛßÝÞÖåçÔÙ×Öáãéáç×ÞÝëæßÛí׿çæãØëÞÞæÜäáèàâçÝàáß×íßÖÚ×ãßäçÜÛìÝÞ×éãÖåîäàâÜá×ÚÜá×ÖìרÜÒãØØÕâÚÙÞæäÞÛâàæÞèàçæÚßéÒÚØÚÞíàäÚãèÑÙÜçÚÞàäÞäåÜçäÝÛâÝèàâÞååèÞêàçÖëÙßÙåáêããê‚-‚)áÖÜéÚÕë×ßÚßÔãââÛëÛãÙÖߨÙàìÛèÛâìÚÓÕâÝóÕêÚâÝááÙãÜíàäãáÔÙäçæÙàçãÞââÛÝÚÚÝ×àé×ß×å××ÜèÙéâÜæØÕÞäàèÞåÖæÝÞæåâéÓÚÜäÛÛíçÜÜÕâàâ‚QÒØÖÜàÙÜìæáÝàÝá×âàÛäåÙÌ×ÔäØåæãØéáÛæáÔãâïçÜØãÛÕÞáÞàÛäÙêÞÛ×ßæâÜàÚßÞÔãÝÝáæÕØáÙæÞÜáãæââåÛäÜÞÓÞÚØÙÙãÒèãØäÛáÕÓúØÝÒâïäÖßÜããÝèÏàãâæÖÝÏßÛÜâÎÝáåÛÜâØÏåãéÞÖÖãØ×æàÙáéãßÞçßÔÜáÞÏäÝÜìáÜãØçãí×èãßÒèÏÔÞÞßÜâãÞÒäÙèááÓîÜÝÜ×áÝæã×ÚÖêìáàáÛßàÞÒããÓèÚïâæÛèÛßãßÛÏÜÛÜàÔÞêÛêÕëèàÚßâߨÙã×ÔàÞÚÛ¾¼ÄÀººÅÀ»½Ã¿½È¼Ã½Â´ÀÆ¿ÂÃÅÄÃÁ·ÃÂÊÈŹÀ¿Ê¿ÄÀý¾Â¿¼ÄÁ½É¿»ÂÀÂÁ»Ï¹¿ÂȺ½ÅÀ¾ÄĽÒÈÒÍÌÐÓËÐÑÐÓËÒÏÐÏÇ×ÑÉÎÎÎÒÍÎóòíâæïèóëöñïóóúóëâÿôíçëìðîçñóôæõóòãôîôÜèõçóôçíçïìóëï÷ãêêøëñîöäÝæëëòçìóíðâòñçììéñåéìôçùñèîìñëøóêîéóò÷õíøðíîíüïõúüñêíçéóêýãôéïéúùçòëïóççòòãíÿïðïùåíõå‚ðìäöíç‚÷øéèéòøèëëàììèìå÷êíéïüñïñìðæîðïëåîéëñéëêïÝïèðüíúõñäêôñéêïæï‚çòëðãìæòôêæíßææþðõçúêðñðùïïòæâùëïðííöã÷ïóàúëîôïìîòùëôôíêìêñóîðøßîïèìôñîíé‚òöñëóîäâïèïéêóëíûîïíïçêëïðóéöóïâúðìõæóôõîÜôåð‚îíõ‚êïîóéóçííïëïçûàñïåèòëìñòúðîõíðìîáòîöñïìñöêßçéæêßïâðñíðãéóçòúòîð÷âñùôïòóéôøëþÞïòìçóöéééø÷îïçòòÚðíßä‚ðçóôòë÷åéòñèüýõîëåéøöãæòñêèåïäèìäëìïåæëêóóíùë÷åå÷êíò÷îëóîàìëðîíéðè÷èéñøéçüõëóîöÛèôããåðìèìöïìôìðòòæå÷÷òèììéñöíõúéêôñêíöéóíìöìðóïçåúõéïôçóûþõìíõìðëñôîóõïêïøîàééûðíéãìùïïæòðòíîå÷íïúèþåçïæôàëíðïôíéôíññòòêëÝïîúçîíåóêäæåñçíôïôëæâ÷îñðêóî÷ûäãïíñåæóðæßìñâééó÷êììâûìïìúèñåïôñóé÷ôäíûÞêððòìôîòôßîõõòåêåíìáãôñöêôìðêçãÿðëä‚ôêêõéëäòõíñáúëàðõéõ÷ôüù÷óðäóôöäèí÷íàëñïíöÞáïêñöëïæïòöçíëéôîõúêïóõòíçòäý÷ùðïåñúýêéìðöðöôôòôëù‚ñçëíëéâññøßîïúïðòòéõúìýêçîãìôïóòúóüíùñôóöòÖõóõòûúæîòóëï÷èìéùýïïúçöæîíöïçúäûòòíæ÷éûì÷ìâóðñøêñóäðãóôòäïîöåíïãïìïîðòêéëïòúéîõîïëûòêùæôë÷ùêôéòìñïîùöøâìî‚ +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 DATE = '2024-08-17T06:05:12.0' / written date (yyyy-mm-ddThh:mm:ss.s) FILENAME= 'CSST_MSC_MS_WIDE_20240817060512_20240817060742_10160100000_09_L0_V01'TELESCOP= 'CSST ' / telescope name INSTRUME= 'MSC ' / instrument name OBSID = '10160100066' / observation ID OBSTYPE = 'WIDE ' / observation type DATASET = 'csst-msc-c9-25sqdeg-v3' / dataset RADECSYS= 'ICRS ' / coordinate system of the object EQUINOX = 2000.0 FITSSWV = '2.1.0_20231208_wcl-5' / FITS creating software version COMMENT ========================================================================COMMENT OBJECT INFORMATION COMMENT ========================================================================OBJECT = '60000000' / object name TARGET = '161953.0+395345' / target name (hhmmss.s+ddmmss) RA_OBJ = 244.972773 / object RA (deg) DEC_OBJ = 39.895901 / object Dec (deg) COMMENT ========================================================================COMMENT TELESCOPE INFORMATION COMMENT ========================================================================REFFRAME= 'CSSTGSC-1.0' / guiding catalog version DATE-OBS= '2024-08-17T06:05:12.0' / observation date (yyyy-mm-ddThh:mm:ss.s) SATESWV = '0001 ' / satellite software version EXPSTART= 60539.25361 / exposure start time (MJD) CABSTART= 60539.25361 / first cabin time after exposure start (MJD) SUNANGL0= 50.0 / angle between the Sun and opt axis at CABSTART MOONANG0= 30.0 / angle between the Moon and opt axis at CABSTARTTEL_ALT0= 20.0 / angle between opt axis and Elimb at CABSTART POS_ANG0= 20.0 / angle between y axis and North Pole at CABSTARTPOSI0_X = -2218.3663 / orbital position in X at CABSTART (km) POSI0_Y = -5817.8559 / orbital position in Y at CABSTART (km) POSI0_Z = 2597.9467 / orbital position in Z at CABSTART (km) VELO0_X = 5.058199026407237 / orbital velocity in X at CABSTART (km/s) VELO0_Y = -3.85818799871231 / orbital velocity in Y at CABSTART (km/s) VELO0_Z = -4.322908628205369 / orbital velocity in Z at CABSTART (km/s) EULER0_1= 0.0 / Euler angle 1 at CABSTART (deg) EULER0_2= 0.0 / Euler angle 2 at CABSTART (deg) EULER0_3= 0.0 / Euler angle 3 at CABSTART (deg) RA_PNT0 = 244.972773 / pointing RA at CABSTART (deg) DEC_PNT0= 39.895901 / pointing Dec at CABSTART (deg) EXPEND = 60539.25535 / exposure end time (MJD) CABEND = 60539.25535 / first cabin time after exposure end (MJD) SUNANGL1= 50.0 / angle between the Sun and opt axis at CABEND MOONANG1= 30.0 / angle between the 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 North Pole at CABEND POSI1_X = 0.0 / orbital position in X at CABEND (km) POSI1_Y = 0.0 / orbital position in Y at CABEND (km) POSI1_Z = 0.0 / orbital position in Z at CABEND (km) VELO1_X = 0.0 / orbital velocity in X at CABEND (km/s) VELO1_Y = 0.0 / orbital velocity in Y at CABEND (km/s) VELO1_Z = 0.0 / orbital velocity in Z at CABEND (km/s) EULER1_1= 0.0 / Euler angle 1 at CABEND (deg) EULER1_2= 0.0 / Euler angle 2 at CABEND (deg) EULER1_3= 0.0 / Euler angle 3 at CABEND (deg) RA_PNT1 = 244.972773 / pointing RA at CABEND (deg) DEC_PNT1= 39.895901 / pointing Dec at CABEND (deg) EPOCH = 2024.6 / equinox of pointing RA and Dec EXPTIME = 150.0 / exposure time (s) COMMENT ========================================================================COMMENT VERIFICATION INFORMATION COMMENT ========================================================================CHECKSUM= 'EhhmGhhkEhhkEhhk' / HDU checksum updated 2025-05-15T14:39:50 DATASUM = '0 ' / data unit checksum updated 2025-05-15T14:39:50 END XTENSION= 'IMAGE ' / image extension BITPIX = 16 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 20000 NAXIS2 = 4700 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups BSCALE = 1 BZERO = 32768 EXTNAME = 'IMAGE ' / extension name EXTVER = 1 / extension version number 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 (K) DETTEMP2= 173.0 / detector temperature at EXPEND (K) DETTEMP3= 173.0 / detector temperature at READT1 (K) DETSIZE = '9560x9264' / detector size DATASECT= '9216x9232' / data section PIXSCAL1= 0.074 / pixel scale for axis 1 (arcsec/pixel) PIXSCAL2= 0.074 / pixel scale for axis 2 (arcsec/pixel) PIXSIZE1= 10 / pixel size for axis 1 (micron) PIXSIZE2= 10 / pixel size for axis 2 (micron) DETECTOR= '09 ' / detector ID DETLABEL= 'r-1 ' / detector label FILTER = 'r ' / filter name NCHANNEL= 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 INFORMATION COMMENT ========================================================================WCSAXES = 2 / number of WCS axes CRPIX1 = 4600.694649742984 / x-coordinate of reference pixel CRPIX2 = 4604.383757040574 / y-coordinate of reference pixel CRVAL1 = 244.5872723135913 / value of reference pixel on axis 1 CRVAL2 = 39.68195466168969 / value of reference pixel on axis 2 CTYPE1 = 'RA---TAN' / type of RA WCS projection CTYPE2 = 'DEC--TAN' / type of Dec WCS projection CD1_1 = -6.8096767434962E-06 / transformation matrix element (deg/pix) CD1_2 = -1.9394088718019E-05 / transformation matrix element (deg/pix) CD2_1 = -1.9394079613547E-05 / transformation matrix element (deg/pix) CD2_2 = 6.81009530128767E-06 / transformation matrix element (deg/pix) COMMENT ========================================================================COMMENT READOUT INFORMATION COMMENT ========================================================================GAINLVL = '01' / gain level GAIN01 = 1.089977689498424 / gain (e-/ADU, channel 01) GAIN02 = 1.099175619835018 / gain (e-/ADU, channel 02) GAIN03 = 1.107649165474623 / gain (e-/ADU, channel 03) GAIN04 = 1.121722214644728 / gain (e-/ADU, channel 04) GAIN05 = 1.081220797968546 / gain (e-/ADU, channel 05) GAIN06 = 1.09456272850753 / gain (e-/ADU, channel 06) GAIN07 = 1.111430472148367 / gain (e-/ADU, channel 07) GAIN08 = 1.105606424366627 / gain (e-/ADU, channel 08) GAIN09 = 1.101874970320761 / gain (e-/ADU, channel 09) GAIN10 = 1.085064046871988 / gain (e-/ADU, channel 10) GAIN11 = 1.12218772525153 / gain (e-/ADU, channel 11) GAIN12 = 1.114934178226512 / gain (e-/ADU, channel 12) GAIN13 = 1.119336504852553 / gain (e-/ADU, channel 13) GAIN14 = 1.093036190194986 / gain (e-/ADU, channel 14) GAIN15 = 1.084334798104931 / gain (e-/ADU, channel 15) GAIN16 = 1.090894538052644 / gain (e-/ADU, channel 16) RON01 = 5.0 / readout noise (e-, channel 01) RON02 = 5.0 / readout noise (e-, channel 02) RON03 = 5.0 / readout noise (e-, channel 03) RON04 = 5.0 / readout noise (e-, channel 04) RON05 = 5.0 / readout noise (e-, channel 05) RON06 = 5.0 / readout noise (e-, channel 06) RON07 = 5.0 / readout noise (e-, channel 07) RON08 = 5.0 / readout noise (e-, channel 08) RON09 = 5.0 / readout noise (e-, channel 09) RON10 = 5.0 / readout noise (e-, channel 10) RON11 = 5.0 / readout noise (e-, channel 11) RON12 = 5.0 / readout noise (e-, channel 12) RON13 = 5.0 / readout noise (e-, channel 13) RON14 = 5.0 / readout noise (e-, channel 14) RON15 = 5.0 / readout noise (e-, channel 15) RON16 = 5.0 / readout noise (e-, channel 16) ROTIME0 = '2024-08-17T06:07:42.1' / readout start time (UTC) ROTIME1 = '2024-08-17T06:08:22.1' / readout end time (UTC) ROSPEED = 10.0 / readout speed (MHz) EXPTIME = 150.0 / exposure time (s) DARKTIME= 152.0 / dark current time (s) COMMENT ========================================================================COMMENT SHUTTER INFORMATION COMMENT ========================================================================SHTSTAT = T / shutter status, T=open, F=close SHTOPEN0= '2024-08-17T06:07:42.1' / shutter open time (start, UTC) SHTOPEN1= '2024-08-17T06:07:42.1' / shutter open time (end, UTC) SHTCLOS0= '2024-08-17T06:07:42.1' / shutter close time (start, UTC) SHTCLOS1= '2024-08-17T06:07:42.1' / shutter close time (end, UTC) COMMENT ========================================================================COMMENT LED INFORMATION COMMENT ========================================================================LEDFLAG = 'main ' / main/backup LED LEDSTAT = '00000000000000' / LED status LEDT01 = 0.0 / LED01 flash time (ms) LEDT02 = 0.0 / LED02 flash time (ms) LEDT03 = 0.0 / LED03 flash time (ms) LEDT04 = 0.0 / LED04 flash time (ms) LEDT05 = 0.0 / LED05 flash time (ms) LEDT06 = 0.0 / LED06 flash time (ms) LEDT07 = 0.0 / LED07 flash time (ms) LEDT08 = 0.0 / LED08 flash time (ms) LEDT09 = 0.0 / LED09 flash time (ms) LEDT10 = 0.0 / LED10 flash time (ms) LEDT11 = 0.0 / LED11 flash time (ms) LEDT12 = 0.0 / LED12 flash time (ms) LEDT13 = 0.0 / LED13 flash time (ms) LEDT14 = 0.0 / LED14 flash time (ms) LEDTEMP = 173.0 / LED temperature (K) COMMENT ========================================================================COMMENT VERIFICATION INFORMATION COMMENT ========================================================================CHECKSUM= 'o7Tkp5Rio5Rio5Ri' / HDU checksum updated 2025-05-15T14:39:52 DATASUM = '3747953988' / data unit checksum updated 2025-05-15T14:39:52 END ÅÄÅÍÍÁ¾È»ÅÇȾÃÁ¼ÃÇÈÃÇÌÁÃÂËÊÚÓÙéæêàéèæÞáÝêæáåå×ìÞäôÓæåâëÛàßÝäèÛîíàßáÝïâÓæ×äãßãæâæêàØÜêâêäéäâãëåééÜêãááßáÙáçÚãàëéÛãÛæêÞÚÓáéðíçëèêÝæçîÜòèççáèâáçîèàåßÚæäàëàÚâÜÞÛãèãßååÚêæíåÞ×ãæíäêçñèæÝãíæïìæåçâ×ÝæÛÕáâÜÒâëâàÕäâÚìîððíääííãçéàâêéßáðöäÜèàêéÞÝçäÙäæÞéáÞãÞàäÛé×âäÜäáØÝèâàÚãÞãîëâçèâäâîàòëßéÙêàèíæàåÞÙãââïåèæÚßçéÖæêÔÝçéçëêæßÕéäàÓäâãæëéæâ×ÖåæèäÝßéæãÐàáçßëÝëÝÚÞÞäæåÕíàÒÖààìéíäêôìðô‚ óìðôïåÑÝéß÷àÛÞÙßëçðïèëñàãÝèêîêÜãåçäáÕáëêÞçòâææêÜîòëØèåèáäáíäïåèäìèßàãßßåÚâñææìæèëääñõêêàÞóðÓÚéÙàÒÞéíæÞíãæââÜáèÝàÓèñáÔÙáàáççáÛáéããæáÜÜáçï×áãìâäàåæîëäÜéäÖÝáâÙáðéØêäèÜâãìííìÛÝÜïâßêæáçèêéäãâÖôáðééíñòîëñêìéñìéæááåÛîäåâßÞÝêàÝâåææÒÞèÛíÛÜèñæéßáßÜêØÜãêããææðéíÙæçâÚâßíðäàí×îÞäðØààÝçæßÞéããÕæâæÚáåÙéääæáÝØàßéåçâéÚÝéîÔæéìââÛÞèááÔâèØæçÙäêáçàâãæÚØâàåÞÚãÙâèå×àßâÝéàÛâÛéäàëàÝãàéâçßÚÛèäæÜçØõÚÞåæÓäçÙëäÝàààææßÝìêäïåâÙæ×ãáåèðßéâêãçæÙèçÞÜ×ÚÝðÜãàÞåâçÐíØÞèãÛçãÛçÕØæÕåðäîïââãæÜÜÞâãâåâÝðìÚßçèÛØÚêõêæëîÕëëÞââ÷ä䨂xåïÛÛåÚêìÙâáàâÞàëÚéîåáâÜôØìØÚÚåÝàÞëÚÕòåãëÚäôàßÝãäåéÝàáæêëâÞèÞâÛéàêâÝèæßâáßååäâòòáéäáéçáâàãØëÒëáèåßëèÞìéåæÔÞåäííçÞèæëâïáÛÞáèáì÷ãÝäààßèñíðã×çÞòèÝëæÞéßçÚߨßãÜÞãàÜàèêêìîèÝ×ðßèåÛãéçêæãÕîÝÛàÞèñÙáäééåäßêîíâØåéâÜÞìåãåéïÞáàñÓçïÏîØáëñçÞçåØçÝÚîãÓæáâçåèÛÝèÛÛÚíàÛäçÙêææåéäâèßçæÞØòèÚáîéïâæåäâéëßåÝæâããáëìåàáÞÚåéàÙóàãßéâæ×éãæßêçáàåæãâá×çéååØÃÂÂÅÁÄÇÄÃÅÌÁ½ÃÁÆËÆÄÊÇÅÄŽÁÅÞÅÇÉÆÉÌÁÈÆÉ½ÂÉÅÆÁÈÅÐÂÁÀËÄÈÂÄÈÄÂÊÆÈÅü¿ÅÆÂÈÄÉÇÉÁÍÁÐÍÇÇÍÊÄÊÇÅÊÉÑÉÊÐÌÄËÐàèèëãðààêèßñêðøâîõïÞæÚêñéëæìãêæïêûéüÞòïèñâÝûääñáïÕßæíîàçëíßìßêîñæëìßéæÞâìèçôîÞÜåÞéëîäíæîéíìíÝàãçÛÜëçèäèÞåæåãçàìèáåßëãçäëßå‚éèäæõèæûÙéäïáãëìßíÛèóôèêñèìæäâìëêÝèíêëàééïëéðîïàñèææâåãîä×éÔÚäêßãìïîãâàéèêêëçêéï‚‚‚ ‚ðæççãäêäéìèÞéðïäîãìîòäÛäéäæèýäàðêóëëíñáîæêÜäëãééæãêêÝÞæåãëðãÜìîàìðíçóóÖßãåìîíìïïáàççëìáóüæåàèïóÞÞêìêéÛêùæìêäïñöößçäáìììëëçéæáéâåÞåææçóéôìîçôçåàêìùæåìàØîâîèéõðìíëêãèïäùëèêìççßõåõêÞÞëêôíäáêæåëêêéêåëåæóåëåèéðççáÕçøëçßÚêéêïäÜßéõèÝæãêêãéæèæäëàãÝæãïíçàëáîåááñöõÞÞëàßßóÜçëéÞêîêæ÷ðèñáðîáøÝêäêçÙìäæâæáÜãêâæîãßçåÝòíñìñÝæÞèíàéÞâîéçéäÛêìèäïãæìîæîçÞìûèéííæàäêêæÜåââØêãìÝáã×ìèééßïçèäçãêâçíñÞìîíàçÝäáïêåèêçëÞíÔäéøæáðâäëêçàáäëàõõéãêìçæáçíçòâãâÜæëääñèíêæóÜåëåßæøãðèéäêæììæäåêñèå×ßÜòçèñéßïø÷ÜõïäèäîèæïßçâèîáÖäÞÜæìòâñåõÝéëåéëáÙããíçéìÞçåäîæíçéñâàîåæçÞðååÞçäåìêæçíèåçòøäíÝê×íì׿êÜçåëîÝîêÛéÛäèáñãåßéäìëêêåûççÛñ×ëéïæçîßßæöæîàåÜéôì‚‚$‚ôêîåÞáçâçÞàññãéãÝëâèêéøñãíéåçíåøæßäõæãæÞØëìßõáåäïëçîâßÞéÙáôæíñõåêèç×êéïãíÞíüîäòêäñÜÝõòëãäòñôáçìäàêäêåÜéèÚßòäâêñØáëßîëôúçðáíïðÚå÷‚cãÝêìäïáçèåçöóáêâëëìæîêÚðïåðåäåâëçñéåãäþâåæûîòèðäëãîáóíèæ÷ìæáîêäíáèßçïìÛëàâëêíãåëãææéÓäëìæîóëååãîáôðçãéîîèëããåöçæåæçñéíÙäÝçèäèêîáççòÞïäââëëü÷ÞæéåéèéâèãíéëáàÝèÜÛàáäíðçðîìöçåâåëëÞáçâóñðââÞæáãåçìÚïèëäëðåçêãèáôôÅѸÌÄÆÌÇÌÅÁÑÁËÒÍËÈËÉÈÏÈÄÈÑÂÉÉÎÌÂÔÊÊÊÌÊÈÈÄÅÈÁÏÁÊÈÅÉÂËÍÆÌÇËÆÆÇÆÉÊÏÆËÐÄËÊü¼¶½À¾¿µ¾½·¶½º½¸¸¿ºÀµ¾À¾¸Á»×ÙÕÓÚÉ×ÏÏÑÜàØÞÎÐÕÔÕÜÚÝÑßËØÜÛÞØÞÓßÓßßÕàÖÝÓÞÚÓÔÔÐÒÓéÒÜØÕÚÏØÖ×ÍÕãÐ×Ï×ÑÙÞÛÒÚ×ÕÜÏÕÐÔÜ×ÍÖÔÖßéÑÛÒÒÕÏÕÔÕÌËßÚÊÒßáÏÚÑÝáÝÙÓÌÝßàÜÞÛÖÝÝßÒØÖÖÚÑÜÛÖËãÏÍÔÔÚÏ×ÚÐéØÌæéÞÔÔÙߨ×רØÐÒ×Ý×ÖÝËâÐÑÑÔÚÖÔÛÊØÐ×áæäßßÚÎÙáÝáÞÑÙáÖÙÕÒÙØÚÔÙèÞÏÜÛÖÚåÞØÔäáÛÛÖâÏÖÖÞß×àÔÐäÞÚ×à×ÖàØÙáÜØàÕØÝÒÞÕÞØÜÙÐáÑÜáÑ×ßÖØÑÚäàÜÖÕÖÞÝÓÝÔàÆâÔÓÏÞÌÐÛ×ÕÛØÙØÙ×ÙÈËÏ×ÚÝÖÜÙ×ÐÔÖÞßÔâÝÑÎÔáäÛÛÜÙÓáÎàæÍÏãÚÜàÔÔâáÖÖØçÖÊÏÓÝÞÖàÑÝÔÓÏÔÔÐÚÓÔßÖÙÙÓàÕÝØÒÔßÍÑÝÒ×ÙØÛØÍÕÞÜÝÒâÝÔÔãàáØÕÛÏÌâÞÔÏÚÎÛÚÏ×ÖßåÛÉßäÜÇÝáÏçØÕàÕÙÑÔÙßãÑÕÜÞÔ×àÛàëÕÜØåäÔÙרÛßßÖÔ×ãÚÛÝßâÐæÙâÊÔÓáÜÞ×ÑÛÓÙÑÙØÒÕÝÙËÖÝêÝÖïÚàÒÒÞÔÕÙÙÝäÐÙÔ×çÔÔÖåÒØáÑÙßÖÑÖÖÔáãØÓÕÔÕÙÓØÙç×ÌÛìÖâßÞÚÜÏÛÛçÐÕÛÊÓÓ×ÕØÛÒÙØ×Ü×ÔÛÝÛÑÓËÞáÎáØÙÛÖÍãÚ×ÚÞÕÈÚÕÞäÕÜáàÚÔäàÔÜÜÎÛÔÏØÍÕßßáÚÕÏÕÌâÈÚÚàÙáÓÃÖÙ×ÔâÞÓߨÖàÓÒàáÜÕÛÜËÐÏÕÓ×ßÊÕåØÑ׿àÚÔÖØáÚØÞÑßÛÞÍÍÎÔãßÔßé×ÕÙÎÉÝØßââÒØÚÖã×ÔàÒ×ÌË×ÚÜÐÏÑÄÓÆÜÛÝÑßÙÚãÚÕÙÚáÑÝçâÖÝÜÙçÌìÓÛÏÚÑÕÔÞÏÖÝÔËÖÐÔÛÕÔÊÍÓØÑÔàåÛÛÖâÔÕÞÞÐÐÏÜØ×ÕÓ×ÚÞÔÚãØÐåÜÛÞâÔÞ×ÞØØÕ×ÕÝåÔÔÜÖÙæÞÞÐßÓÙÚÙÊÜÕÛÎÚÚÞÏÙÎÛÝÚàÏ×Ù×ÝÖÞàÞ×ÛÚØÞÚäÐÛØÚÚÛàâÑäÚÛ×áØÒÕÞÖÏÚÕÍ×ÓÄÛÏÓÜçàÛÓÝÕÛìÔÓÔÜÎÞ×ÚÔßËáäÏéßâÝÒËæâÞÚâÙÜãÑ×Ô×ËÕÓØÎÖÑרÛÎÏÓÛÓÚÝäãÓÙÝØÓÖÌ×ÓÍÚìàÖÛÙèåÏÜÖØÌÚÚÚÝÚØÑÌ×ÕÜ×ÜÝßËÞàÔÓÞÚÔߨ×ÝÖÛØà×ãÞÛÛàÉ×ãÜÚÜ×ÕéâÞÛÞ×ÛàßÞèÝÝàÓ×à×ÝÜÙèòÖö‚÷î÷âØÙÛØÓÚÙ×ÙÊÊàÞãÊÜÄÎäØÓÙÌÖßÓÞÔÒàÏÜÍÝÛÞÖáßáØÞÕÚÔÓÜåÛâ×ÔØÒÝÙÞÖÔ×äÛËߨܨÞíàÊÜÙÔÞ×ÕÙÝÒÛÙÓÙÝÜÚéÏÈÝÓÒÙÔÖÕÎÛ×ÕÙÎÖãÝæþ‚÷áíäÝÕĶľ¾Ã´»Ã¸¾¶º¼·¼À¿¹¸¹Ä¾»¿ÃÀ·»À¸³ºµ¹¾Âºµ±¶¶Ã»¹¶¹¹´¶·¶Å··¾¶½¿ÆÃÀÁ¸¹»º½¶¹´º¸®±¸·´·¹±¶¹»ºµ±º´·¸¶Âº¸°³¹½ÌÐÊÊÔÕÒÍÖÌÙØÔÑÊÓÚÔÔÎËËÙÓÐÖÒÓÕÛÞÒæÙØêÏÜËÖ××ÚÑÒÎÍÓÏÒÊÚÜÛËÖÏÓÕÐÒÓÍÌãÑÒÌÓßÒÞÚÏÓÑÐáÆÖÎÜÌØÔÐÍÒÚ×ÔÖÔáÍÓÚÚÏ×ÝÒÎÙÑØÏÐÛÑâÓÒÉÍÑÔÙ×ßÝÏÓÊÜæÖÞÏÓÜØÔÛÛÕÝÒÜÑÔÛÙÖÖÎÙÙ×ÚÒÑÓæ×ÔÖÐÔÌÛÝÞÎÜÈÙÚÓÍÖßÖÕØÕÖÞËÓÓÒÔ×ÓÛÚÓÊÑÍÉÉÔßãÊÝâÖàØÛÖÜÔßÔßÖÔÔ×ÉÖÙÔÌÜÐÏÔßÝ×ÕÑÕÖÕÔÏÔØÑÖÏÓÒÖÖÊÐÅÕÐÒ×ÔÒÕçÝßÑÜÚàÏÊÔÌÜÇÉÎÎÓÙÝÏÕÒØ××ÏÚÜÏÚרÑÏÒÙËÐÓÓÒâÝÎâÔÄØÒÔÚÑËÕÏÐÚÍÖÛÚáØØÔÓÍ×ÏÏËØÆÐÔÛÒÕÖÌÓÚÊÐÏÎÓרËÞÕÜÔÍÙàÛß××ÍÕÏÅÍÒÏÙáØÔÕâÙÌÜÓÓÕáÒÐÕßÐÒØÎ×ÒÒ×ÚÚèÌÜÞÞ×ÕÊÑÒØÛÏÜÁÕØßÎØÉÙÔãÓÝÑÌ×ÔËËË×ÏÔÕÏÆÝÄÓÏØÌ×Õâ×ÙÔÎÔÜÍåáÜÜÎ×ÖßÖÙÊÏÎÐÒØ×ØÕÙÝÖÕÐ×ÌÓÖÎßÐÖÕÔËÙÛÝÝÝÅÌÎÊÖÖÚÖÛÉÛÝËÙÔäÐÐÓäÐÚÒÖ×ÔØÉ×éÖØØÉÓË×ÔÝ×ãÙßÖèÐÙÑÓÖÕÔÚ××ÖÞäÉÎÒØÑÌ×ÎרÐÏÏÜÕÙÒÛÕÙÕÌáÕØÐÏÒÛÝÙÖâÚÕÍÓÉÓ××ÍÔØÖÑÛÒÏÎÞÖãÙÞàÛÑÒÐÌÚÍÚÓÓëØÕÝäÍÎÌØÓÖÚÜÎàÖÑÙÐÎ×ÓÚÔÎßâÉÇØÑÝÜØÏÑÍ×ÏÙÌÑÎÎÓÝßÚÛà×ÑÏÓÏÕÔÊÝÖÌÕ×ÚÒÕÞÓÓÐר×ÔÞÒÖÞÐÖÕÌÔÛÒÏ×ÕâÜÒÞØÛÛÑÒÓÑØÜÔÜÝ×ÏÔÉàÖÔãÍàÔÜÔÏÎÛÕßÐÚÔàØ×ÏרÓÕáØËâÀÕØéÌÓáßרÐ×ÙÐÑÔÖÒÍÖÏèÕÙØÑÎÓÌØÏÑÏÕÑÝ×ÝÚÖâÜÓÜÓáÐÞÖÎÓÕãÛÛÑ××ÌÖÖÒØÓÎÑÝÓÑÓÖØÖÌÐ×ÓÔÝÕÕÙÛÙÑÛÌÒÍØÏÝÊØÝÛÌØÖÖÛÜâÕØÄØÒÜÚàÕËÓÓÏÖÕÚÙÓÓÑÝÖØèÜáÞÙÕØÈÌØÍ×ÛÊßÔÐÖÚÍÖÕáßÍÛÏÙÛÞÔÎÇÔ×ÎÑÑÅÖÒÔÚÞÔÙÑä×ÔØËÒÕÓÝÐÎÐãÚÔ×ÖÐÖÓàØØÐÐÕ×ÛÚÐÓÏáÖÜÖÛÒØäÛÒÞÛÎâÕâÎÖËËÍæÖØÇÔÍÕÙÐÎÒÐÐÔÔßÖÕÓ×ÍÜ×ÒÓÛÓÜÖÔÒÏÉÝÜÅÛ×ÚÓËßËØÓÒÍÑÙÓÖÉÑÚàÚÙÐÕÔÔ×ÔÎ×ÌÏ×Ì×ÑÊØÚÖÕÝÑÒÛÏÙÙÑ××àÚØÙÐÈßÕÙçð‚ ìÐÐßÌØÒÛÓÕÎÖ×ÏÐ×ÔÕÎÒÜÉÖÒÖâÙ×ßÔÖÖ×ÚÖ×ÉØÔÙÑÔãÓÇÜÓÙÔÙàÜÚÚÛÍÔÞÛØÑÖÐÐØÕ×ÜÜÕÚÉÏÓÖÛÚÓÊÝÌÙÒÔÑÖµ²¹®º²µ²µÀ¸´º¿²µ´»¶¸¼³´·º·´¹º·¹Á·¹º¸¿±²µ±µ°¹»­Ã´¸¹º¸¾¼ºµµ®µ¼º¹µº´¿ºµµ¼ºÉÔÏÂÎÐËÎÎÆÊÈÌÉÈËÒÍÐÇÐÑÆÍÎÊÍàìåèãëêèêîàæàæèåíéöñìêéâïññÞõáïæíêìëåóøáç÷éñõáçììèéòüçëçëñíìíúåèêäßñåäìáëÜóèÜðíâáóíòçåáñáçãìíçãïÛÙåÜëãìéíßóââñöâßÜîìéñâçèìëõõåáåáõÝñíèßëçíããéâðîõéñðøôÝïïíêêæ÷éæíé÷êàèáñéçéâèôëæäçëàæêôæóèïíêêàóñëôòåâîóçùù÷Õðçðî÷êéòïàëêëëõçôæìäïàâòãñæääùéëðéæçîçåðéñèäëêììææòíòßÝðÝêõÛÛêèêïßäðìæÝãñêòðáäùêõíêçíôçõîÞêåèíçêóïíëáêåîúîîëôàäåçéçìãëêççêèïíéçíãããïæëóìåõõõàßæèíÜåàéòðìäîèøöëäêêñíùíìâðïðæìôêßãòäðëãïîôãâêîàêóùÙìéêúßìøæòèòðñãîáîÜäâðéäâéîëäãíéæìÜåèéùÝòÝéìàîèåíãã÷ãäññäáùåæõßíèëðééÝòêèÙèý÷áñçíäéæçðåÝæàøæðéàáåóçóñÞáæåçüíôïõãîîõíäôçèîãããáêáñãóéëçìîäïåãæìéææáèëóêãóãæêéßåèÛèßêãðäêâÞìîæñæöçôìñîñÞïïíêíìïâðìæàèëìòðóåèåñîïêåêéèéíæçíåèÞéëéëííøÞèþìîûøðçéèßãëãêëÚëèëèôåëóìñîíãïýóïàçéñéééãêäûäèâúäíïèÙßíççèêëãîëíèéÛäòãåèçåïíïèíèöåâíííãêìïëíêñ÷àçîãêðéÜðæçíãæãéîÜÞãäÛãáàêîìãëÝëÝææôëûæïîããßàñìïçòèëñëåõßéññèôëÜæÜåêñãçÞäóïîòÝêêõØéðó×ïèêèìçåðèíïäïçîèáäêßõêòæáõÖæãåéåóàåëêâÝïòõïééìêîïêáçâàñîåëéêèâÖðèÙéïïâå÷äïæâñëèäêóéãæææççñíããéâåèèßëìæìíæÝíðâêîðëðîùïåíÙöòáäâãæäâñâàéóïÞéïêáïïåèåáçééîæåëëè÷ääõäåçãïìáõéóäçéãåìäêéçíøõëíááææâÝáàÞïãçôãïîíïóìèáéòñáèæàëèîìøáùéòçñêêÜØéìñçøìïáöêáîùòãêúüîèæáðáøëëâóçõíòèìãçîîèéëéßæÚòèîëæáèßàéåêäæíééñòéîóîåäçêæððæòææìÞíôÜóæáíåééçíóöõðëøêèòúåàóìæêïßìòòôñëîËÅÈÍËÊÀÒÄÃÍÅÏÏËÊÔÌÉÊÏÎËÉÆÆÍÌÒÍÊÇÓÕÈÏÃÌÒÏÓËÆÊÏÆÌ¿ÌÄÇÆÒËÌÆÏÍÈÊËÏÆÂÌÃÉÂÈÑÉÏÈÍÍÈËÎÏÌÑÌÛÔÏÏÈÐÓÑÎÑÊÊÇÌÊÐðëóëã÷äûçíéõåíéíìñçÙàèò÷éîêëöñÞõñõóôëäïæîîéñëôëíúóñôåëèäíèæëùëìßóòèèòíõôèîçåâêòðëòöøêþâúë÷êñÝðçõãîíïîþéô‚òæäèãêçïêñõóãñÞñååîòæõñöðôïúíþèæïæíéíêéðéðäéøòãäìòåêæéëïðíéûì‚ó‚sðåìôäëññäñíîíñæö÷òòåàòëëôîòèöíäÝáòìõ÷èðîïííèêíêãóñóìøîñîôõäçöðììéûúñëñéïððìóùñýâûöëðäóòïöóããßîåæòéðíïñöæïêòëåèíëòóêèìðîèéúöâïåôïåèôñèöðëçñæíìòäåèêõêòå÷çíýèðòòçíè÷öóåóåöáîñëöçðóéíóôçïêìóõîîôäìòðïôéôíìïîïèïðíèâìæñãìêéëéííåìõûâóìñìïìõðôóíîöáæ÷óúóßäóûçîîûäÞíèöìèéíëñêìíìèëâäìõèíïñêêèâïåêãññ‚èßòùëâçíìôèîùóîðèøúïæéöíñîêïôçêëèîìæçáãóêðïçäîìóõ÷ïïïïñõïóëñìòèðíùóúóæöóçîãæðêùçúîøâüéíæôñïæ÷ëëêø÷èëñöðáìùúåëôöçóäëõóíéëéøèôæøìéþåüëæîîêìçîñäëåðíæñðýíïäãñîéêîäñìöèøïîôð÷æïøôçÿñôõëîîóúêéñíóíöñóîúèóñâéóíéíâææöîðíèóøãÿçèõðëñðæîâéëéèíïæóéïéäöéóäåîóñúòîéêðîììëòæßèääöáíííðâêéïæç÷óûäé÷øâòæñïí÷ïòßùëóâæéñóý÷ùñêæüñíéîøñéïöçùêìïìïéôñíäìçðéóóñóøéçê÷õåêüéêêïììæìæêëíúóæòîçðéðóôêòîòæîèôáïåõê‚ìúìçæ÷êäî÷óíî‚ñëãôôóò÷îàíôéèúäæáëêóãôêíéëèåñèøíäöòêóíëëåòôíòëðøõîééçèçâëçÞéîãêíòéôçìóæíîùàìðëñîìæîïïêïèëêï÷óûìòíñíðóðïÞóåøÝ‚íûçðêãîïâôçõñðëóçæíòòéïûêåëûëóîíîìïòßíõñïèïëêííèñæêêèçòîíñìñèòøéáóåéøçõéäêòåìéäñð‚êâæ÷èîïöõëäéêóæèéîðøæþîæìëìöãðôçèâæðñàëøðæñîðõñî÷íõèîèïíúâòëîÓòëîõæïìóë÷óóáëíèâæóòîîåîøôóæõïÚèéçèééäïæøèëñïóçóÒÎÖÆÊÔÏÎÓÅÍÇÏÈÑÐÔÒÐÔÓÕÏÌÍËÉÈÔÏÒÑËÖÒÕÈÑÍËÓÐÊÓÒÓÌËÎÐÚÑËÖÖÒÏÓÒÕÒÎÔÕÔÉÍÎ×ÊѶ»²¼¯¹·À½½´²·´½·¸µ·¸¹ºµº¸¹³àÙáÝ×ßßÜÞÞØßÖÝÌÝÜÐÖÒÖ×ÍÑÙáäÙÞÕ×ÈØßÕãÕÓÍÐÞ×ÜÝÔÍÐÐÜâÛÌßàÛÌáÕßÙØÌÓãæÕàÜÓáÛÑÇáÛÕÕÈÒÌÒÙÕÊÚØÔÕÝÙÞÝâáÝÚÞÆÜÚÙÓßÚÙÑäÕÜÍÒÝÙÚÓÔßÒÞÜÔâÚàÕÜרâÖäÔÙËÝÙÔÕÏÑÝßÙÜÏ×ÔÕÙÚÎÚÐÌÓàÜÜÞÝØà×ÜËÔÝÔÙ×ÓÑÖÚÖßÚÖÕâØÜÍÛÒÛÖÛÖÚÞÖÛÊÝÕÕáÜÓÙÕÔÓÞÓÙÖÙæàÍåÅàÛ×ßÌãÛÐ×ÕçæÖÐ×ÙËÛÓÚÕÞÒÙÙÖåáÞÓåãßÙØØÐÒÞÕÛáãÕÕÝâØÚÜÛÜÖÙ×ÙÕÒ×ÝÝÙÜÚàåÍÜÖÞÍÜÔÖÔÔÓÞÒÛáÖ×âÙÚçÛØ××àÙÛÎÖáÔ×àÑÌÓÓæÌÜÏÝØÔÍáØáÔØÙÐÔÙÝáÒÜÙâÞÐÖÝêÕÛ×ÕÞÝÚÚØÛáÙÞÒáÔÏØÚæÝ×ðÒÒÐÛÜÞÏÚÝçÔ×ÐÜØÐÖÑÛêÒÝÓÝÒßã×ÔØØÞØÜ×ÚÑÇÞÑÙØÍæåÝÐÓÛØØÖÞÜØÓáÕÖÛÉÍáÖØàêäÕÖçâÙÝÑÚÞØèÞäÛÒÛÍØÔÜàâÕÜæÚÑáÜàäÑÜ×ÙÚÞÛàÙÕÚÖàÛÕ×ÚÙÔÕÖÝÏÜÏÒèÖßÞÕÐÔçâáëÕÖÕÑãÊÖÓ×ÊÏÍÏÚØÕÍÑãÛÝÛÚÝâÖÐÛÑÓÊÓÔÌÔæÛÞ×ÔÐÛà×ÏÝÙÕÖ×áÔÞÖÍ×ÔÛââÓÛÝÔÜáâØÝÏßÕØÒÐÈßèÌÎÛÖÍ×áÝÙÚâÈàÒÝÐÖÐÚ×ÑÐáÝÔ×ÓÔÎÜÍâ׿ÕÔÛËÛÐÙËßÎÙáÜÞÙÚÞ×ßÜÎÚØëÜáÒ×ÜÒßÛÚÓÚÛÒß×ß×ÐÐ×äÙäÎØÖÚÙâÞØÞÙÚØÑÔÎÔãÏØÑÕÒÖËÐÆÓÛåÎÞåÛÝÑÞÕÚÙØÐÒÙ×ÕÝÙÞÑëÛÞÛÜâÔÓßÖÜÕÏåÏØÓÒÒÏÒÙÞÝäÝÚáÑÓÚËÔÓÝÓÎÑÙÝÑÛÎÚÔÞÕÐÙâÖäÞÒÚàÔÕÎâÚáÛÙÞØÝÝÒÝÛØÔáÛâÏë×Õ׿ÌÓçÊÛßáÛØÝÛ×ÎÕÊáÚçÜÞÔÐèÈÝ×ÕÐàÞÜÔßÚÓÙÚÛ×Û×ÍÚÖàÑâÜÍÚÜÓÔÚÒÏÓÕÖÚÔåÓá×ÑÓÕØÎØÖÓàÙÚÛØÓÏÖÝÜß×ÙÙÜÙèÙÝçßÔÐß×Û‚EÚæÈß×ÏÇÒØÝÌáÙäÔÞÏÏÜÛÜßÛáÜØ×ÛÜáÍÕÙÛÚáÕàÒÕàãØÛÑáÓÏØÐÛÏÝßÝØÙÝÖ×ÝÞãÜßÞÖÉÓâáÊÑÔÕßáÕÙÖáãÏâÜÒàÐàßàÉàÎÐÏÎÖÕÙØØâÐÕ×ÓãÞáãÑØÒÖÒÔØÛäÝÑÚâÑÜËÑÛÔëÖÙÝÕÚÍÑ×ÎÐÈÚßÔÐÔÕêÙ×ÖØÛÕÖÚÚÛàÓÚäÚÏÍÏÓÏÚÒÒÓÝÜßÕáÔ×ßÞßÜÔÙØÜØØàâàÓÛáãÕ×ÛØëØÑߨÚ×àÒÝäÖØÖÐÜÙÐæã×ÖÝÖÐãàÐàÞ×Ò×ÛÚÜØâÒÏáÕÖÎÛÑÓÕÖ×ÞÝÙÖàÕµ¾·»º¸¸¶ºÃ¼¿¿º¶¹¼¼µ¼»À¿¶À½·ºÃ¿¶·Á¸¾¾Â¿À·»º¸»¿´ÁÀ¶¸¹Âý¼Â»º¾¼¸»··»º¶Â½³¼ÊÅÂÆÀÌÈÈÀÉÅ¿ÃÆÅ¿ÐÈÄÄÀÆÆÉÇÉ»çåØåÛÞèëÝ×âÚëèââëáàäçêìØæðáÛíçåïçÝÝéñàÚáääñãå×ãìæãäØßÒßÝ×Ü×îæßåâÝïãàØæÛäéáåÜàòÞÞÝÚééÝåòúÚìîì×Üâß××ÞåëÜàéçÛàåìèÞâÙÝçèÜèàããÜâãççãÙÝââñÝìÚêÚçèæïèìäâãåÞÝäãßÞëðéÜáÖáßéçä×ÞôßããçðßáçæàÔèåÛåèçïÕæÝÜÞçÜíæáåäæÛÝãéÚìØéïáëߨãÜÓÞåÜÖëààÙëäáÛêÙâäéççÝäëëçâÞñÜÝÝáèâüÜÞÙØèèÛáæäáÞéãäßîäßêåêÞçàæÜàèâéßáäêñÝñìçÜîåßääàóíÝãßÝîéÙìÞÞêßÙÜÕßåçãÜâãåèâÚçìëÙïèÜåâ×ñæäãàãæÜÖóßàÚÖåÖîÕêââßÞßèêÜéáÙòììåèèàðçäãåÛÚãÍÝÖÜëåÜáéöãÛã×âäÜåçåìæäÙêäæåãåâïÚâßàäèÓëäêäââççììäÞíåàÛôéåÖâÛèìêÙëÙàáßæÜÛìÝÜçßéÙÞâéÓãêâàæïîàÜèíéÝÕîïããßåãâÝÜíèáÝèæãßÝëèëíãØãÞäíáÚàêßëçáçØÝÞßçéÜßÚèìäêëæÜáãääÞßÞÚçéçãÜæÜàèáàæàâÔæéåéßæÙéÛãäÜÑäÞßãñðæÞßâåêïâåéëæíìâÜãçÜñàõäçêæòêçâãåÜàãêâæáÚàò×ëãä×ãæéãÖëâæâÚìâàæçÛèèäßîãÞàÜÙáãÝäåÛåçÛôÙñêäÞéàåâààßßåÕæÞèÝâáàèêÞÛÝåãØãäÛØØááðæ×ßÞãëÕçåàãêÛçí×ïîÝáØê×ÚÛããëàðàÚØêØâëåçÛãêèð÷íÝåççäéÝÜçáéîßëåäàãìæãÙßëßÞ×èßìèÐÜãÛáÝÛåÜâææØàâÞãëåàâáÞãÞåäàááæÜåàéëêåæãÚñäæåßÜÚäæ×ß×àåÕßðæåÛààìåãÝçãâãäåãâçÛæçÞÜÕßáÜ×åèêã×ß×ÖåßìàóéåÙàÝöÝáÝðÞåäæàãéâáäìñáêáèìèëäÜÞÜÙçßããìåÜÛáçàâáßßåÛæàãÝìÛàçßÚÖæèããàÛØèäÜßåáðæêáÞáàñÞàãáêÖäââ×ÙâãçßåãæîßÞçÝáâéÓêÛÔÝíÜÞßÞàÙäáßÕßïçíÛßá×åìàåíáêãïáçæÝéìÙÝéÙêáðåÝêÞÞãñÜàÚâìàëëåÓïéà×îâìäàÜâÙÜæâáÜæáããåâØñëáÒêÝáßãÞÖÞæèßáåëáãÞãÛäßååàÞÝäèéæéÙàæÕÝèÖÙæíáêãìßÞÜáäëðáàßíðå×ãáêíÛÜäæãÝìäàëãéàèàéäßèà¿ÁÈÊÈË·ÇÂÆÁÆÁÂÏÅÄÃÁÇÄÆÂÉËÈÅÈÈÅÿÀÂŽÃÈÇÁÂÁÎÇÁ¾ÆÇÉÊÉÂÉËÅÊÃÁÃÂÄÇÂÈÂÆÊÊÃÊÄ»¾ÅÇÇÄÄÂÌÁ½Ç¾ÆÃÉÀÃÃÅÂÂÄÂÀ¾¼áÙÞÛÝæÛÜÞáÙæÜæÙÚâÙáäÜãÙæâäãáØáÓæãöÑÞÞÔáÜæáÞÍáäÜâØÝÝäÜÜÜæÝÞßà×ÜâäâéÜâéÙÖâ×àØÝæââêÙØëØáÞëáÚßçßçàäñäåÕÞãà×íéêÜÜÝÏéÝæÛÜÞÖåçãÝìæÛØäêèÓãÖÚÜèåñàÝçáàÝãÙâßÒáãßæàéßâàâäÜàÜèâÛíÚåÜÚâéàÝÚß×çåçÕæÙÝà×è×ëäçäÕåÞߨäØÛæÛÚÜàéÛâÞßÜâÚäßÞàæäæáâÝßäÖåÜê×ßÒÝ÷üÜìßÍåÚäàæçÝàÚÙÓÖçßàÛÝêÚáåÝØÖÚãáæÝäÞåéáÝÛÝàáâæØáéìçßÞêäÞäââæäÔéÛåÖéìÝäÖÛìèå×ßêëÞëèÕàÙòÕàÝßáàåàêãÜãïàÞäÝâÛîáßäâáßêáæáîãïâßÜàâçàäàÜëáëÞãÕíßãêåàâäéæÚØëÚÛêÝîÛðßäÙÙåÖÞêìéÞâáçêÛØÕßòÜØÞ×ÖàåäçåâåäÚäßâêÝÚééçßãêåâÜâãáÞàÙîÞêÞìÝáÕßáäÞÝéØßáçØÜâßåäØÛçåØ×ÞæÙáÝìðØèêëßäßèßëÓÝêÙÞçÕâðÞãâÎÕãÛåãÖÏ×çÚàìëåßðâèäàäìÞÔàÚéòïÓâçÔÓàÜãßãâêàåß×ÕæØäÙæàØÞÜéèæêàÕæâÖßâÝÚéåÙàÝàÓÖäâéäéÝçåï‚àÔÕâÞâãÜÞÛîàßåñÜàÒàÜߨãæáòëçßñßáæáÚÝÖêèÕÝãßÜÚàßéÝéÜÚàØÚéÞäáÞäààéãç×ÓßÝÚÜߨÜáàäåÞÕ×ÝæåðäÙÙßéÙéæååêââáåÞäÛßäÕäÔéÝßãóÚçãÜààãèåÛÓçèççãááÝÛæàÛÖÍäáåæáÞêàäáààäáßרâáåÛëáÙÙÛãÛçæäáíÙåØÔØßÝØÜÒÙÝâßçÙÝìåÞããÞÛäôæðûäßäãìäéÔïçØæÑèàäïãÜåßï×ååÙæÙÖçåÙáãæßçÝáÜàáÛéääàÜÝÝÚçÛáàÝÚãäÞãçßÙèãåæßâàÖèäÝïÚÛâÛãßáÔîêãäæåáåãææßáÛäãÛàÙåÝåæÞÛìæåÛØêÙçØßéäßãáéãçØêççÞßäâéìãêÙæäáèâßáéßÐÝèíáØÙÝïáõèÜäßßÝêæçéòáçêÏÝÜíëÚÞáèéÞéåáàåÙÚæÑéßßáÞÛèæëââèÞãâßãÛâÛåÛØæèÜÜÜÜßâéÜååâÕÔãäßåÞÛäæßãßååàÞàçÓáàèÞâÖòáÚÕÝäßÞÔßßàôßÜÜãØèææßßÛÚáëÜïçÜåàäâåØÚäÙÙØÝÞàÚÕÛåØÑÙåÝêßàãæéÒèÛâáÞåëëçÞâÕàçÝäåêèßÕâåàØäàãÜØàëÛêåçëÞáêâó‚ìÝÇ¿ÀÄËüÁÄÀÉÄÆ¼ÂÅÅȹÊÐÀ¼ÆÁÈÁÍÀÀÀÄÁÆÑƽÄÃÃÁÁȽ»ÀÆËÆÇÃÃÂÂÃÁÁÄÉÅ¿¿ÌɼįÇÄËÈÇÌÂÊÅÌË¿ÎÇÊÉÂÈÇËÈÆÆÑÉÒÐÄÏðêðãëæëæáääãçðïñæìçäðÝïîèôãóíèææäáìßÙòçìõöèàêåôòÜíåëèæíâóíèìðáñèçêæöêçáãæããìØæáâïèëçñçâÞûÛèâÞÜêáÝ×ßíãðóòæØÞéäòêèâÝðéëûêëíãêóäåíèêðãØêëæääóéîááâôèíäæîèæâäÞàåâçèêçæåàááßéíçæäïÝ÷ñäðâáéöéÝîîåáàíïçãíååñáúåòìëíôìîöæàèéãåëîáÛïçðëäóàéèôîçäõïíàíñãæàâéãäççãéõëìêðêôéæíïëîíàïëôìôíÜîÞëçíåõâèëæìæèâæÛÕßüêìêöæáîêêÖëãëßñåðÝíìÜèòõáèëãäæçìóíåáÜëêáÙêíÝèîóàèñýéÞííçäáèýêòîáóàáÞàäãèíßèìöèãéëïÛêßîäééæåñïäáéæíêÞäíèéáïçðæãïêãïïÝêåèóôäèêìèìîéÞíçëåëéâò÷æìëëìèëÝêîèîæìèàáàìßéìíìÞëáéîúõïçñõîòîãòãåßÞææéíëâáçìàíåìíâèòÛèëëëêðÞóúíÛÕéìíóãéëö×èêòàêæåñèðíòòõäïãñóéçæñãóêñçßÜáÞîéèèééæòöðèßïôìÛçíéôÕöóãëèøéíâãðìïôèçåØðôïéìõìçìí÷áïêçåæãåìóñíôååÝéÞÝðçîÛàåáñõïÞéæäâäãâáìíìëìäãùæÞæìëîæñìäÝâáëåÕÜçæêíëóåâêæåÚóéëßîêåéãìáæîôßãìãëèúâêåíåãçíðêâåïñâÙêÜàâðãâßíÙõãèæÛÚëáìåìãëîçÖâðíñêÙãÚåêçÙêöñíêæÞþåìäêïõåæçûåàêéèêâîâæòòîìèâõêâêëæçßßæäæçááøëÞîêçêÛëçéóíòòìâÞèÞæéâÿêïáåõîøèïéíâÙêìèçåí‚€ïãæåâàëéëëìïðåëåíîâñíèðâæÞßéç×éîèêïéâïßîÛãîäê÷ïêêâçêëõàïìñßáèææðÛáäéìåàóèÝäåäìîñçíÝíêèåæìñîëñëôäæåõéòâé×éêí×ÚäÞæëðæãòóíÝïäëåðâêàíùàçëäâìäîõêùòïã‚ãîòäòõÿñðò‚ï‚‚‚%‚E‚0‚‚‚,‚*‚)‚*‚‚*‚‚‚%‚1‚(‚‚‚‚ ‚‚ÿ‚ú‚‚‚‚ ù‚ñú‚þøðóïë÷áùíèêæïÞççôÞæñíèÞñÝíòãîìÖëííëæçäæëçíééãøìëâçìëÚâÛäíëçÜâìäñêèèáñîòïóìçóæóâÝæàôæåäðâãáüíåõïõÛöóâðíçßêçìãåëàèèäðßéãôéçÑÆÌÉÏÉÆÊËÓÎÅÊÔÆËÉÉØÎÒÉÍÉ¿ÏËÊÆËÄÐÀÊÉËËÇÈËÊÉÍÊÉÌÌÑÎÉÎÅÂÄÄÓËÇ¿ÍÎÑÃÅÐÍÌÃÆÎÀÂúżÁ·À¶º¿ÀºÁÁµº¶»ÁÆ¿À±ÓæàÜÖÚ×ââÍÕâÚÚÕåÖÒæÔÙÚÐØßÍÛØáÙãÚÚäØÔרÜèÓßäØßÍâ×ÝÖåÙÚÚÙÚÚÙæÜÚ×ÞÚØßÑâÓØÜØá×ÓÝáÙÞØÜÓáÒãÚÞêÔØßáÐÔÑÝÝÛáæÚÕÝàÛÜÚÔÎåÏÑÑÒÙãÝßÞÞáÈßææÞÖæàãÓÞÚÜâÝ×ÓÜÛ×ÚâíÝÜéÓÐááÜáÖÌ×ÕÙÒßáÔÕÜÙÔáÝèÖØÝÝõâÏ×ÛÛ×à×ßÜá×ÚÞêÐÛâÞÐÕÙËÞÒ×ÙèàÒßðÖߨàÐÔÝáÔÚר×ÙÛÕäÖÕÝÞÖÚãÖáèÄÞØÖÌØÒÙâÕÞÕÚÕàáàÖäÕØÞçäÔãØÒÜãÕÝÞÔäÚÞã×ÜÔäÒÔËãæáÛÕ×çÞßßÛÝÑäÌÜèãÑØÞáÔÙÞÏßÏäåÞÕÙÒÞÔÛàÛÓÞàÍØÞèÔàÕßÔÖÖÜÞàÒèÙØÚÑàåÛÝîÝóÕÛÕéØÚéáàÝÖÚËÙÕÞãâêÍÚæØÏÝÞèéÚßàÚÒÜáÝâ‚?ßÖÏÓØáÝÕÞÜØÔàÐÝàÑÏßÚàÜâÜÝÖÕÚÕÙêÛÚÓÎÞ×ÙåÝÝâߨ×ÜÝÛäØÙâÚÒâØÔßÎÕâßæåßáÜß×ÝÙáÕèàÒÝ×áÝÚÓèãÑÌßáëßÓ×ÝÖàáåÝÞ×àÌÚÜÓÞØáÔÖÚàØÛÑÖáÓçãÛßÜÛ×ÙÓáçÏÓÐÙÜ׿ÜÙÙãÞÝÛàãáÕÞÓ×ÔåâÞÌÚÖâà׿áââ×àÕÙÖÖÜÑÜèÛÔÕÞäÞߨØÝÓÕãÝäÍÞÕ×Ü×ÛÛÒÓÐàÜ×ÑÖáÛÚ×ÕÚãÏãàæØæÑÜàéÙÏÕâåÓèØÖïÛÛÝÙßßÕÐÙØÙÜÍÕæÙÞàåÙâÝêÛÖÍÚâÜÙ×ÕÙרÓÓÜÜãÞâÓߨñÚÔÒàÝÚÕÛÜçáçåéÓÕÔÛÝáÚÚÏÜàÕÛÛÙ×ÜÝ×ÕßÔÓÜÕââßâÓßßãÝÖ×ÝâÊçá×ãÚÞÙæË×ÛÜàà×ÔæåרààÐÚØÝçÖâÚÔáÓØÙרâÚÝßßÝÓ×ÜÐæßáâàÎåÍÖÇßÙã×ÕåÏáÔÔÝÜÓÚÓÞÌÕáØÝâÙÍÚÙÚÑÓäÜØÞÜÚÚÕ×åàÜ×ÓÜÜÛÝÑÞÙÜ×ߨÚÈÜàæÚÚÓÝßãâàåÖÚÏÓÜåÝÙÓèàÎÚ×ÙÕàâÖâÐæîÓàÜÑÞÛÙÕÐÛàÖØÛÛÖÚßߨÞÚàÔÛÞÛÏÛ×ÕÙÏÚÏèÜØÛßÓèáÕØíÛÜÕÙÙÚÖÖãäÖÕàÕêåרâÙÓâÎÖÒÙÕäÓÑÙÚàÕÚ×ÜâØÙÙÛßÜÙßÞßàÔÜÜÍÜÛßßÜâ××ÜÔÙÔèÚÜßÜ×ÜÜÜÓÜßÚÜÓÝØÖáÏåÝÕáÚáÞàÕÑÒßÛäßàËÖñßÞÙÞÙ×ÚÛÖÚÖàÔæßÛØåÚÞÛãæÞÞßÞÞÞÐÙÝääÝßÔÖÜÝÔÛÞÍÙÛÔãÏÚèÚÑÕÐÙÏãÙÜÖÝÕÙÙàäáåÕØÔ×ßåàÜáêãÝßÖãÑäÌÞÜÎÞÑÜÝÒæäíãàÕæÜÓåÜãÝØéßÞ×äÞÝââÚÜ×ÐÕÝíÛÓàÒÛÕéáàÌÞÙÚ½¿¾¾Àº»½»½ÀüÂÀ¿¹¼¸Ä»À¼ºµ¾º¸½Á¹º³¼»½¿Ã¾Å¾º¾ÃÀ¾Â»¼¼¼·¾¸¾¿À¼¹Ã¾Ã¾¼Åº½¾»¿ÀÁ»ÅÆÊ¾ÅÿÄÃÃÈÂÅÁĽ¾ÂÅÂÈÆÉÆâÜåëèÜçÛßÞîãÝêäÒÕØéëòàÛãàåìæëçæÜÙéáèØéßÝØÞâßêÛàëìØÕÝåØÜìèäâäÚê×åâæáÛæàßÞÜÞì×ãÝãÙíïçòåããÝéèáØèààãâçèçßàÝæÜÙïçÞ×äîâÚÙãçÖàÜæÚâáçâçâéèÝÔàáàÞ×ïââÝÛÝèìàîÙãßÜçßæÞçèââÙçéçààÛäßêáãàëââéîåäÜäâåâØ×ìÚèìÛÞëçàãäáâÛÞÝèÖÛáççØÝߨäëÛÝçåÝäáàááÚçÜêÞÞÝßÝèàÞâäÙØìÙßÚêåßìÞá×âëæÚÞêæêå×ßÑèãâÛæãââàáãÛãïéàêÚßæâ×íÜÚÛëçÙÙêâßáØëèãÔéãèëÚèåÜéêæåßÛÝáååÞÛåÝéàÞÙäãÜåÝáëÙââÞßîæßìãéäèÙèèáÙêìñáâëáèÝêÙâáâÛáçãäåàäêôÞçâÞÞáìâÙåßñäçäàãäâÜååôÜëáìáßÖÞÙÞæëãÛßÓßàâåÜìÝÕáâáëâÞßåÞÜâØÞÛÚæéÝåêæÚÞâàà×ääîîåÕÝæçÝÖäÙëàîÞÒâÙàÞâîåÚÚíéååÜãßßäØáÑßÞâæìÛÞãÞÛäëëçæõÛôçäÜñÞâèìåîÕæÙàÚÝÛÞé×íëÚÞéÕäçîëéÞæâÜâããßÛåáÝÝãóéù‚þäîÜçêåÞìÝÜøÝîçåÔÝéâëÚ×åéñÇÞÚäìçæãØäëÛéìáààãàääîèÙÛßçÛèëî×áÚÚàåãçéèêÛçèÛëèÝâÙØæÚØäçÜèéàçæâííßáâàèêÙÞãÞÙäØàØÚâççâáàáâàÕñçáäÞÓèÝéêéÛÞèåêçãÝïíåßçÝããæÖìèïßåáßãÙáôéÞìÝ×äÙäáåæÖâßÞâðçßßçä×âéãÙ××ßæÜéÚÝÜáÞâáãèßÛáÙéèßÛèíìâàÜäàáÜáàÞÝèçäèâêâÜÛîá×ãéãêáèàßàòëéçÛàßëèÞÞÛÚÝîæåÔäßìëáàúäéáøëÜßãÜÞãÜèãáòïãâååèèÛóÝÝéäÚåÐÝßâêäêêßãòáéÝäâØáÛêæç×ëåÞàÛååÝæßÛè×ÞåçãàäáÙêàÚäèæÔâîÚÞääæìäßãÝåçìÝääåÚàÞáæëäâñäè×ÙáâãÙáÙæÛåàèßÔߨ‚ìßáÜÝàÞãàâßèèàçßéàÛâãêßææàèáØòÞâÞÞïäáÝçãðäóäèßäãÛêàèæíìÝçäÞææçèÛåàáâçèóîý‚‚b‚è‚¥‚7‚ðôíäïéïçÞìàãØÚçæáçâÜååÜáÜßãóÜääááÜòàéÞÞåãáçÞåØâïëäåëäÚèáÛâàáÛÞäêèãìãîÖäÜåäêÚäéÜãæèæíæäÛèÚØáéàÞäãôéèàåöÝÝèççâàèêêéçÛëÜçÄɾÂÉÍÃǼÅÈÐÅÄÐÃÆÄÉ¾ÇÆ¿ÌÂĽÅ˾ÂÀ¿ÂÆÉľÁÁÅÅÆÁ¾ÍÃÈÁÁËÅÀ½Ê¿È¿ÈÊÁÃÀýÅÅź¿Ç¿¼É¿ÂÂÁÇÂÄþȿſ¿Â¸¿ÅÆÅĿŽãÜåæóæÚØèñâàââéÕäßãààëùäàéèÞÝÚßÛíåéäóØÛçÞØØãÝ×ÜàèÚßâäÚãÖæØàïäéëÙáÖÛÚÏðààÝåíÖØßäáäãàÜ×ãêäèãç×ÜÞã×ÞÙÞçÜÜäÜäÚߨçßÝãáÑÚåÕçßâãÜÞìÝå×îâØÛÞäãßÛßåÕãÚâàãßèçäáöçöåï×â×ååßßááïÝÖÔãÝÚØáèØÝÙìàÔóÖçßÐÛãèãØØÞæÚÛÝãàçÙ×äÙÙåÞäà×íã×ÞÜÞßßÙÜÙàãàåãÞáçÕíÖßÚÜááàÛáÜáÕéßÚÛÖéÞÖãàßÛáèèåäÜÒØÕÙÛØÖÚåÚÒÙçâÝççáÚâÙæÖÐãæåÝçëÜÏæãÝØßÕèÚïåÞÑÛâäÔÞâÛÞÙÕÞÚÛ×áÞÕÝÝåØàíàáéÚéÝÕâÜèëÜäæâÙßèÙÛÚÜÕëÜÒÔßÒáØáÎÓÝÚäÛÛáØÔãßàÞíîÝìÜâÔ×Öë×ËÓÖÝÚÖäÔÝäßÝÛéâåëÝÛáêàÍÜÙßãæßÕàÖæÝêãäâåÚÛÖåßÞââà×ààÛáâßëæàáãÐÚßÒÖãæÛÞÛÕåÜè×çîèáçÝåßéàãåæßÙÒàãØÛÞàäÑÛÛÛÙäàÒßÛÞæÛàßßäáàçèÜäáçÔÜÙÚ×âåääÛíîßÒÓÖàÜÒçäÜÎÚáãàßÚèÛÜÞåãÔåâÝÞåßÝâÛãØâÙááæäáÝÙÛàÚÞÜáÏÞØßááÛèÜÜßÝáàßàÞìÜäåÝßéáóèæÕØåÓßÚÞÞàïáÐéÛåÜÜçÙáÞæÜÛßÝÞÖåçÔÙ×Öáãéáç×ÞÝëæßÛí׿çæãØëÞÞæÜäáèàâçÝàáß×íßÖÚ×ãßäçÜÛìÝÞ×éãÖåîäàâÜá×ÚÜá×ÖìרÜÒãØØÕâÚÙÞæäÞÛâàæÞèàçæÚßéÒÚØÚÞíàäÚãèÑÙÜçÚÞàäÞäåÜçäÝÛâÝèàâÞååèÞêàçÖëÙßÙåáêããê‚-‚)áÖÜéÚÕë×ßÚßÔãââÛëÛãÙÖߨÙàìÛèÛâìÚÓÕâÝóÕêÚâÝááÙãÜíàäãáÔÙäçæÙàçãÞââÛÝÚÚÝ×àé×ß×å××ÜèÙéâÜæØÕÞäàèÞåÖæÝÞæåâéÓÚÜäÛÛíçÜÜÕâàâ‚QÒØÖÜàÙÜìæáÝàÝá×âàÛäåÙÌ×ÔäØåæãØéáÛæáÔãâïçÜØãÛÕÞáÞàÛäÙêÞÛ×ßæâÜàÚßÞÔãÝÝáæÕØáÙæÞÜáãæââåÛäÜÞÓÞÚØÙÙãÒèãØäÛáÕÓúØÝÒâïäÖßÜããÝèÏàãâæÖÝÏßÛÜâÎÝáåÛÜâØÏåãéÞÖÖãØ×æàÙáéãßÞçßÔÜáÞÏäÝÜìáÜãØçãí×èãßÒèÏÔÞÞßÜâãÞÒäÙèááÓîÜÝÜ×áÝæã×ÚÖêìáàáÛßàÞÒããÓèÚïâæÛèÛßãßÛÏÜÛÜàÔÞêÛêÕëèàÚßâߨÙã×ÔàÞÚÛ¾¼ÄÀººÅÀ»½Ã¿½È¼Ã½Â´ÀÆ¿ÂÃÅÄÃÁ·ÃÂÊÈŹÀ¿Ê¿ÄÀý¾Â¿¼ÄÁ½É¿»ÂÀÂÁ»Ï¹¿ÂȺ½ÅÀ¾ÄĽÒÈÒÍÌÐÓËÐÑÐÓËÒÏÐÏÇ×ÑÉÎÎÎÒÍÎóòíâæïèóëöñïóóúóëâÿôíçëìðîçñóôæõóòãôîôÜèõçóôçíçïìóëï÷ãêêøëñîöäÝæëëòçìóíðâòñçììéñåéìôçùñèîìñëøóêîéóò÷õíøðíîíüïõúüñêíçéóêýãôéïéúùçòëïóççòòãíÿïðïùåíõå‚ðìäöíç‚÷øéèéòøèëëàììèìå÷êíéïüñïñìðæîðïëåîéëñéëêïÝïèðüíúõñäêôñéêïæï‚çòëðãìæòôêæíßææþðõçúêðñðùïïòæâùëïðííöã÷ïóàúëîôïìîòùëôôíêìêñóîðøßîïèìôñîíé‚òöñëóîäâïèïéêóëíûîïíïçêëïðóéöóïâúðìõæóôõîÜôåð‚îíõ‚êïîóéóçííïëïçûàñïåèòëìñòúðîõíðìîáòîöñïìñöêßçéæêßïâðñíðãéóçòúòîð÷âñùôïòóéôøëþÞïòìçóöéééø÷îïçòòÚðíßä‚ðçóôòë÷åéòñèüýõîëåéøöãæòñêèåïäèìäëìïåæëêóóíùë÷åå÷êíò÷îëóîàìëðîíéðè÷èéñøéçüõëóîöÛèôããåðìèìöïìôìðòòæå÷÷òèììéñöíõúéêôñêíöéóíìöìðóïçåúõéïôçóûþõìíõìðëñôîóõïêïøîàééûðíéãìùïïæòðòíîå÷íïúèþåçïæôàëíðïôíéôíññòòêëÝïîúçîíåóêäæåñçíôïôëæâ÷îñðêóî÷ûäãïíñåæóðæßìñâééó÷êììâûìïìúèñåïôñóé÷ôäíûÞêððòìôîòôßîõõòåêåíìáãôñöêôìðêçãÿðëä‚ôêêõéëäòõíñáúëàðõéõ÷ôüù÷óðäóôöäèí÷íàëñïíöÞáïêñöëïæïòöçíëéôîõúêïóõòíçòäý÷ùðïåñúýêéìðöðöôôòôëù‚ñçëíëéâññøßîïúïðòòéõúìýêçîãìôïóòúóüíùñôóöòÖõóõòûúæîòóëï÷èìéùýïïúçöæîíöïçúäûòòíæ÷éûì÷ìâóðñøêñóäðãóôòäïîöåíïãïìïîðòêéëïòúéîõîïëûòêùæôë÷ùêôéòìñïîùöøâìî‚ éëüêí÷âøèééùìùâèêêîûõõíîêñúïùðøûïñíúõû÷þôîôñ÷ôöóóþýîóôöø÷öõ÷ù‚‚‚ÿ‚ ‚‚ñû‚‚‚ ‚‚‚‚ ‚ ‚‚‚‚‚‚‚‚‚‚‚‚!‚‚‚-‚‚!‚‚‚‚‚‚‚‚'‚1‚‚5‚*‚‚‚‚1‚#‚.‚&‚‚ ‚"‚‚‚‚!‚ ‚‚‚‚ diff --git a/observation_sim/instruments/Filter.py b/observation_sim/instruments/Filter.py index 6a01ca972d59ec767694de21009f769ae160c2ff..9e5083d8a7ff3ed38dcb0a18a4da6756cbf460c0 100755 --- a/observation_sim/instruments/Filter.py +++ b/observation_sim/instruments/Filter.py @@ -45,7 +45,6 @@ class Filter(object): 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 @@ -143,7 +142,7 @@ class Filter(object): 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): + def update_limit_saturation_mags(self, exptime=150., psf_fwhm=0.1969, skyFn='sky_emiss_hubble_50_50_A.dat', chip=None, full_depth_exptime=None): if self.filter_type in _util.SPEC_FILTERS: return if chip is not None: @@ -161,5 +160,8 @@ class Filter(object): 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) + if full_depth_exptime is not None: + self.mag_limiting, _ = _util.calculateLimitMag(psf_fwhm=psf_fwhm, pixelSize=pix_scale, throughputFn=throughput_file, readout=5.0, skyFn=skyFn, darknoise=dark_noise, exTime=full_depth_exptime, fw=full_well) + print("for filter %s: mag_limiting: %.3f, mag_saturation: %.3f" % (self.filter_type, self.mag_limiting, self.mag_saturation)) diff --git a/observation_sim/instruments/FilterParam.py b/observation_sim/instruments/FilterParam.py index fe109274d48f88d71a8f97cfc1004aa4858f829b..ac433f36e0984dbe185497b2a72f60b7c65bbbee 100755 --- a/observation_sim/instruments/FilterParam.py +++ b/observation_sim/instruments/FilterParam.py @@ -25,7 +25,7 @@ class FilterParam(object): # 6) sky background: e/pix/s # 7) saturation magnitude # 8) dim end magnitude - if filter_param == None: + if filter_param is 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], @@ -37,9 +37,9 @@ class FilterParam(object): # [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], + "GU": [0.0, 0.0, 2550.0, 10000.0, 1.0, 0.037, 14.0, 26.0], + "GV": [0.0, 0.0, 3500.0, 6500.0, 1.0, 0.037, 14.0, 26.0], + "GI": [0.0, 0.0, 5000.0, 10000.0, 1.0, 0.037, 14.0, 26.0], } else: filtP = filter_param diff --git a/observation_sim/instruments/FocalPlane.py b/observation_sim/instruments/FocalPlane.py index 90c2e0e7372cc2d398ab694248364e8fb73da097..8987fcb91cdf1bbf08dd9bd5ede8956049ee61db 100755 --- a/observation_sim/instruments/FocalPlane.py +++ b/observation_sim/instruments/FocalPlane.py @@ -9,7 +9,7 @@ class FocalPlane(object): self.nchips = 42 self.ignore_chips = [] - if bad_chips == None: + if bad_chips is None: self.bad_chips = [] else: self.bad_chips = bad_chips @@ -80,7 +80,7 @@ class FocalPlane(object): 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): + if (xcen is None) or (ycen is None): xcen = self.cen_pix_x ycen = self.cen_pix_y diff --git a/observation_sim/instruments/_util.py b/observation_sim/instruments/_util.py index 953d229d49e04e8818427d8c26fb9e12fbf80780..0947b0a83bab377e781fcebd90807ca47f2c4f87 100755 --- a/observation_sim/instruments/_util.py +++ b/observation_sim/instruments/_util.py @@ -46,7 +46,7 @@ def photonEnergy(lambd): 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: + description: param {*} aperture: unit m, default 2 m param {*} psf_fwhm: psf fwhm, default 0.1969" param {*} pixelSize: pixel size, default 0.074" diff --git a/observation_sim/instruments/chip/Chip.py b/observation_sim/instruments/chip/Chip.py index e50b6640133512bba4da952d5e80613d01d6eda8..ba6bf3b172cc6e4f20e5c866021205705425b14f 100755 --- a/observation_sim/instruments/chip/Chip.py +++ b/observation_sim/instruments/chip/Chip.py @@ -191,7 +191,7 @@ class Chip(FocalPlane): """Return the filter index and type for a given chip #(chipID) """ filter_type_list = _util.ALL_FILTERS - if chipID == None: + if chipID is None: chipID = self.chipID # updated configurations diff --git a/observation_sim/instruments/chip/chip_utils.py b/observation_sim/instruments/chip/chip_utils.py index cb16118e6916d39a850521f6f164c019cddccf3d..4225521d1c4aa71792df63b735e63741328248af 100644 --- a/observation_sim/instruments/chip/chip_utils.py +++ b/observation_sim/instruments/chip/chip_utils.py @@ -173,6 +173,13 @@ def get_flat(img, seed): return flat_img, flat_normal +def get_innerflat(chip=None, filt=None): + from observation_sim.mock_objects import FlatLED + led_obj = FlatLED(chip, filt) + flat_img = led_obj.getInnerFlat() + return flat_img + + def add_cosmic_rays(img, chip, exptime=150, seed=0): cr_map, cr_event_num = effects.produceCR_Map( xLen=chip.npix_x, yLen=chip.npix_y, @@ -206,7 +213,7 @@ def get_poisson(seed=0, sky_level=0.): def get_base_img(img, chip, read_noise, readout_time, dark_noise, exptime=150., InputDark=None): - if InputDark == None: + if InputDark is None: # base_level = read_noise**2 + dark_noise*(exptime+0.5*readout_time) # base_level = dark_noise*(exptime+0.5*readout_time) base_level = dark_noise*(exptime) @@ -237,7 +244,7 @@ def add_poisson(img, chip, exptime=150., seed=0, sky_level=0., poisson_noise=Non img.addNoise(poisson_noise) # img -= read_noise**2 - if InputDark != None: + if InputDark is not None: # "Instrument/data/dark/dark_1000s_example_0.fits" hdu = fits.open(InputDark) img += hdu[0].data/hdu[0].header['exptime']*exptime diff --git a/observation_sim/instruments/chip/effects.py b/observation_sim/instruments/chip/effects.py index 21735ede4ef8e5307fc645d75c4592b9e0c0e2d5..12f8ae356cec4390c1e83d050893571f2f4747c2 100644 --- a/observation_sim/instruments/chip/effects.py +++ b/observation_sim/instruments/chip/effects.py @@ -3,7 +3,8 @@ from matplotlib.pyplot import flag import numpy as np from numpy.core.fromnumeric import mean, size from numpy.random import Generator, PCG64 -import math,copy +import math +import copy from numba import jit from astropy import stats @@ -22,7 +23,7 @@ def AddOverscan(GSImage, overscan=1000, gain=1, widthl=27, widthr=27, widtht=8, NoiseOS = galsim.GaussianNoise(rng, sigma=read_noise) newimg.addNoise(NoiseOS) newimg = (newimg+overscan)/gain - newimg.array[widtht:(widtht+imgshape[0]),widthl:(widthl+imgshape[1])] = GSImage.array + newimg.array[widtht:(widtht+imgshape[0]), widthl:(widthl+imgshape[1])] = GSImage.array newimg.wcs = GSImage.wcs # if GSImage.wcs is not None: # newwcs = GSImage.wcs.withOrigin(galsim.PositionD(widthl,widtht)) @@ -37,11 +38,11 @@ def DefectivePixels(GSImage, IfHotPix=True, IfDeadPix=True, fraction=1E-4, seed= # Hot Pixel > 20e-/s # Dead Pixel < 70%*Mean rgf = Generator(PCG64(int(seed*1.1))) - if IfHotPix==True and IfDeadPix==True: + if IfHotPix is True and IfDeadPix is True: HotFraction = rgf.random() # fraction in total bad pixels - elif IfHotPix==False and IfDeadPix==False: + elif IfHotPix is False and IfDeadPix is False: return GSImage - elif IfHotPix==True: + elif IfHotPix is True: HotFraction = 1 else: HotFraction = 0 @@ -50,28 +51,28 @@ def DefectivePixels(GSImage, IfHotPix=True, IfDeadPix=True, fraction=1E-4, seed= NPixBad = int(NPix*fraction) NPixHot = int(NPix*fraction*HotFraction) NPixDead = NPixBad-NPixHot - - NPix_y,NPix_x = GSImage.array.shape + + NPix_y, NPix_x = GSImage.array.shape mean = np.mean(GSImage.array) rgp = Generator(PCG64(int(seed))) - yxposfrac = rgp.random((NPixBad,2)) - YPositHot = np.array(NPix_y*yxposfrac[0:NPixHot,0]).astype(np.int32) - XPositHot = np.array(NPix_x*yxposfrac[0:NPixHot,1]).astype(np.int32) - YPositDead = np.array(NPix_y*yxposfrac[NPixHot:,0]).astype(np.int32) - XPositDead = np.array(NPix_x*yxposfrac[NPixHot:,1]).astype(np.int32) + yxposfrac = rgp.random((NPixBad, 2)) + YPositHot = np.array(NPix_y*yxposfrac[0:NPixHot, 0]).astype(np.int32) + XPositHot = np.array(NPix_x*yxposfrac[0:NPixHot, 1]).astype(np.int32) + YPositDead = np.array(NPix_y*yxposfrac[NPixHot:, 0]).astype(np.int32) + XPositDead = np.array(NPix_x*yxposfrac[NPixHot:, 1]).astype(np.int32) rgh = Generator(PCG64(int(seed*1.2))) rgd = Generator(PCG64(int(seed*1.3))) - if IfHotPix==True: - GSImage.array[YPositHot,XPositHot] += rgh.gamma(2,25*150,size=NPixHot) - if IfDeadPix==True: - GSImage.array[YPositDead,XPositDead] = rgd.random(NPixDead)*(mean-biaslevel)*0.7+biaslevel+rgp.standard_normal()*5 + if IfHotPix is True: + GSImage.array[YPositHot, XPositHot] += rgh.gamma(2, 25*150, size=NPixHot) + if IfDeadPix is True: + GSImage.array[YPositDead, XPositDead] = rgd.random(NPixDead)*(mean-biaslevel)*0.7+biaslevel+rgp.standard_normal()*5 return GSImage def BadColumns(GSImage, seed=20240309, chipid=1, logger=None): # Set bad column values - ysize,xsize = GSImage.array.shape + ysize, xsize = GSImage.array.shape subarr = GSImage.array[int(ysize*0.1):int(ysize*0.12), int(xsize*0.1):int(xsize*0.12)] subarr = stats.sigma_clip(subarr, sigma=4, cenfunc='median', maxiters=3, masked=False) meanimg = np.median(subarr) @@ -82,8 +83,8 @@ def BadColumns(GSImage, seed=20240309, chipid=1, logger=None): rgxpos = Generator(PCG64(int(seed*1.2))) rgdn = Generator(PCG64(int(seed*1.3))) - nbadsecA,nbadsecD = rgn.integers(low=1, high=5, size=2) - collen = rgcollen.integers(low=int(ysize*0.1), high=int(ysize*0.7), size=(nbadsecA+nbadsecD)) + nbadsecA, nbadsecD = rgn.integers(low=1, high=5, size=2) + collen = rgcollen.integers(low=int(ysize*0.1), high=int(ysize*0.5), size=(nbadsecA+nbadsecD)) xposit = rgxpos.integers(low=int(xsize*0.05), high=int(xsize*0.95), size=(nbadsecA+nbadsecD)) if logger is not None: logger.info(xposit+1) @@ -91,47 +92,47 @@ def BadColumns(GSImage, seed=20240309, chipid=1, logger=None): print(xposit+1) # signs = 2*rgdn.integers(0,2,size=(nbadsecA+nbadsecD))-1 # if meanimg>0: - dn = rgdn.integers(low=np.abs(meanimg)*1.3+50, high=np.abs(meanimg)*2+150, size=(nbadsecA+nbadsecD)) #*signs + dn = rgdn.integers(low=np.abs(meanimg)*1.3+50, high=np.abs(meanimg)*2+150, size=(nbadsecA+nbadsecD)) # *signs # elif meanimg<0: # dn = rgdn.integers(low=meanimg*2-150, high=meanimg*1.3-50, size=(nbadsecA+nbadsecD)) #*signs for badcoli in range(nbadsecA): - GSImage.array[(ysize-collen[badcoli]):ysize,xposit[badcoli]:(xposit[badcoli]+1)] = (np.abs(np.random.normal(0, stdimg*2, (collen[badcoli],1)))+dn[badcoli]) + GSImage.array[(ysize-collen[badcoli]):ysize, xposit[badcoli]:(xposit[badcoli]+1)] = (np.abs(np.random.normal(0, stdimg*2, (collen[badcoli], 1)))+dn[badcoli]) for badcoli in range(nbadsecD): - GSImage.array[0:collen[badcoli+nbadsecA],xposit[badcoli+nbadsecA]:(xposit[badcoli+nbadsecA]+1)] = (np.abs(np.random.normal(0, stdimg*2, (collen[badcoli+nbadsecA],1)))+dn[badcoli+nbadsecA]) + GSImage.array[0:collen[badcoli+nbadsecA], xposit[badcoli+nbadsecA]:(xposit[badcoli+nbadsecA]+1)] = (np.abs(np.random.normal(0, stdimg*2, (collen[badcoli+nbadsecA], 1)))+dn[badcoli+nbadsecA]) return GSImage -def AddBiasNonUniform16(GSImage, bias_level = 500, nsecy = 2, nsecx=8, seed=202102, logger=None): +def AddBiasNonUniform16(GSImage, bias_level=500, nsecy=2, nsecx=8, seed=202102, logger=None): # Generate Bias and its non-uniformity, and add the 16 bias values to the GS-Image rg = Generator(PCG64(int(seed))) Random16 = (rg.random(nsecy*nsecx)-0.5)*20 - if int(bias_level)==0: - BiasLevel = np.zeros((nsecy,nsecx)) - elif bias_level>0: - BiasLevel = Random16.reshape((nsecy,nsecx)) + bias_level + if int(bias_level) == 0: + BiasLevel = np.zeros((nsecy, nsecx)) + elif bias_level > 0: + BiasLevel = Random16.reshape((nsecy, nsecx)) + bias_level if logger is not None: msg = str(" Biases of 16 channels: " + str(BiasLevel)) logger.info(msg) else: - print(" Biases of 16 channels:\n",BiasLevel) + print(" Biases of 16 channels:\n", BiasLevel) arrshape = GSImage.array.shape secsize_x = int(arrshape[1]/nsecx) secsize_y = int(arrshape[0]/nsecy) for rowi in range(nsecy): for coli in range(nsecx): - GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y,coli*secsize_x:(coli+1)*secsize_x] += BiasLevel[rowi,coli] + GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y, coli*secsize_x:(coli+1)*secsize_x] += BiasLevel[rowi, coli] return GSImage def MakeBiasNcomb(npix_x, npix_y, bias_level=500, ncombine=1, read_noise=5, gain=1, seed=202102, logger=None): # Start with 0 value bias GS-Image - ncombine=int(ncombine) + ncombine = int(ncombine) BiasSngImg0 = galsim.Image(npix_x, npix_y, init_value=0) - BiasSngImg = AddBiasNonUniform16(BiasSngImg0, - bias_level=bias_level, - nsecy = 2, nsecx=8, - seed=int(seed), - logger=logger) + BiasSngImg = AddBiasNonUniform16(BiasSngImg0, + bias_level=bias_level, + nsecy=2, nsecx=8, + seed=int(seed), + logger=logger) BiasCombImg = BiasSngImg*ncombine rng = galsim.UniformDeviate() NoiseBias = galsim.GaussianNoise(rng=rng, sigma=read_noise*ncombine**0.5) @@ -139,7 +140,7 @@ def MakeBiasNcomb(npix_x, npix_y, bias_level=500, ncombine=1, read_noise=5, gain if ncombine == 1: BiasTag = 'Single' pass - elif ncombine >1: + elif ncombine > 1: BiasCombImg /= ncombine BiasTag = 'Combine' # BiasCombImg.replaceNegative(replace_value=0) @@ -147,32 +148,32 @@ def MakeBiasNcomb(npix_x, npix_y, bias_level=500, ncombine=1, read_noise=5, gain return BiasCombImg, BiasTag -def ApplyGainNonUniform16(GSImage, gain=1, nsecy = 2, nsecx=8, seed=202102, logger=None): +def ApplyGainNonUniform16(GSImage, gain=1, nsecy=2, nsecx=8, seed=202102, logger=None): # Generate Gain non-uniformity, and multipy the different factors (mean~1 with sigma~1%) to the GS-Image rg = Generator(PCG64(int(seed))) Random16 = (rg.random(nsecy*nsecx)-0.5)*0.04+1 # sigma~1% - Gain16 = Random16.reshape((nsecy,nsecx))/gain + Gain16 = Random16.reshape((nsecy, nsecx))/gain gain_array = np.ones(nsecy*nsecx)*gain if logger is not None: msg = str("Gain of 16 channels: " + str(Gain16)) logger.info(msg) else: - print("Gain of 16 channels: ",Gain16) + print("Gain of 16 channels: ", Gain16) arrshape = GSImage.array.shape secsize_x = int(arrshape[1]/nsecx) secsize_y = int(arrshape[0]/nsecy) for rowi in range(nsecy): for coli in range(nsecx): - GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y,coli*secsize_x:(coli+1)*secsize_x] *= Gain16[rowi,coli] - gain_array[rowi*nsecx+coli] = 1/Gain16[rowi,coli] + GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y, coli*secsize_x:(coli+1)*secsize_x] *= Gain16[rowi, coli] + gain_array[rowi*nsecx+coli] = 1/Gain16[rowi, coli] return GSImage, gain_array -def GainsNonUniform16(GSImage, gain=1, nsecy = 2, nsecx=8, seed=202102, logger=None): +def GainsNonUniform16(GSImage, gain=1, nsecy=2, nsecx=8, seed=202102, logger=None): # Generate Gain non-uniformity, and multipy the different factors (mean~1 with sigma~1%) to the GS-Image rg = Generator(PCG64(int(seed))) Random16 = (rg.random(nsecy*nsecx)-0.5)*0.04+1 # sigma~1% - Gain16 = Random16.reshape((nsecy,nsecx))/gain + Gain16 = Random16.reshape((nsecy, nsecx))/gain if logger is not None: msg = str(seed-20210202, "Gains of 16 channels: " + str(Gain16)) logger.info(msg) @@ -190,22 +191,22 @@ def GainsNonUniform16(GSImage, gain=1, nsecy = 2, nsecx=8, seed=202102, logger=N def MakeFlatSmooth(GSBounds, seed): rg = Generator(PCG64(int(seed))) - r1,r2,r3,r4 = rg.random(4) + r1, r2, r3, r4 = rg.random(4) a1 = -0.5 + 0.2*r1 a2 = -0.5 + 0.2*r2 a3 = r3+5 a4 = r4+5 - xmin,xmax,ymin,ymax = GSBounds.getXMin(), GSBounds.getXMax(), GSBounds.getYMin(), GSBounds.getYMax() + xmin, xmax, ymin, ymax = GSBounds.getXMin(), GSBounds.getXMax(), GSBounds.getYMin(), GSBounds.getYMax() Flty, Fltx = np.mgrid[ymin:(ymax+1), xmin:(xmax+1)] rg = Generator(PCG64(int(seed))) - p1,p2,bg=rg.poisson(1000, 3) + p1, p2, bg = rg.poisson(1000, 3) Fltz = 0.6*1e-7*(a1 * (Fltx-p1) ** 2 + a2 * (Flty-p2) ** 2 - a3*Fltx - a4*Flty) + bg*20 FlatImg = galsim.ImageF(Fltz) return FlatImg def MakeFlatNcomb(flat_single_image, ncombine=1, read_noise=5, gain=1, overscan=500, biaslevel=500, seed_bias=20210311, logger=None): - ncombine=int(ncombine) + ncombine = int(ncombine) FlatCombImg = flat_single_image*ncombine rng = galsim.UniformDeviate() NoiseFlatPoi = galsim.PoissonNoise(rng=rng, sky_level=0) @@ -215,15 +216,15 @@ def MakeFlatNcomb(flat_single_image, ncombine=1, read_noise=5, gain=1, overscan= # NoiseFlat = galsim.CCDNoise(rng, gain=gain, read_noise=read_noise*ncombine**0.5, sky_level=0) for i in range(ncombine): FlatCombImg = AddBiasNonUniform16( - FlatCombImg, - bias_level=biaslevel, - nsecy=2, nsecx=8, + FlatCombImg, + bias_level=biaslevel, + nsecy=2, nsecx=8, seed=seed_bias, logger=logger) if ncombine == 1: FlatTag = 'Single' pass - elif ncombine >1: + elif ncombine > 1: FlatCombImg /= ncombine FlatTag = 'Combine' # FlatCombImg.replaceNegative(replace_value=0) @@ -232,7 +233,7 @@ def MakeFlatNcomb(flat_single_image, ncombine=1, read_noise=5, gain=1, overscan= def MakeDarkNcomb(npix_x, npix_y, overscan=500, bias_level=500, seed_bias=202102, darkpsec=0.02, exptime=150, ncombine=10, read_noise=5, gain=1, logger=None): - ncombine=int(ncombine) + ncombine = int(ncombine) darkpix = darkpsec*exptime DarkSngImg = galsim.Image(npix_x, npix_y, init_value=darkpix) rng = galsim.UniformDeviate() @@ -243,15 +244,15 @@ def MakeDarkNcomb(npix_x, npix_y, overscan=500, bias_level=500, seed_bias=202102 DarkCombImg.addNoise(NoiseReadN) for i in range(ncombine): DarkCombImg = AddBiasNonUniform16( - DarkCombImg, - bias_level=bias_level, - nsecy = 2, nsecx=8, + DarkCombImg, + bias_level=bias_level, + nsecy=2, nsecx=8, seed=int(seed_bias), logger=logger) if ncombine == 1: DarkTag = 'Single' pass - elif ncombine >1: + elif ncombine > 1: DarkCombImg /= ncombine DarkTag = 'Combine' # DarkCombImg.replaceNegative(replace_value=0) @@ -261,27 +262,30 @@ def MakeDarkNcomb(npix_x, npix_y, overscan=500, bias_level=500, seed_bias=202102 def PRNU_Img(xsize, ysize, sigma=0.01, seed=202101): rg = Generator(PCG64(int(seed))) - prnuarr = rg.normal(1, sigma, (ysize,xsize)) + prnuarr = rg.normal(1, sigma, (ysize, xsize)) prnuimg = galsim.ImageF(prnuarr) return prnuimg +def NonLinear_f(x, beta_1, beta_2): + return x - beta_1 * x * x + beta_2 * x * x * x + + def NonLinearity(GSImage, beta1=5E-7, beta2=0): - NonLinear_f = lambda x, beta_1, beta_2: x - beta_1*x*x + beta_2*x*x*x + # NonLinear_f = lambda x, beta_1, beta_2: x - beta_1*x*x + beta_2*x*x*x GSImage.applyNonlinearity(NonLinear_f, beta1, beta2) return GSImage -######################################## Saturation & Bleeding Start ############################### - +# Saturation & Bleeding Start# def BleedingTrail(aa, yy): - if aa<0.2: - aa=0.2 + if aa < 0.2: + aa = 0.2 else: pass try: fy = 0.5*(math.exp(math.log(yy+1)**3/aa)+np.exp(-1*math.log(yy+1)**3/aa)) - faa= 0.5*(math.e+1/math.e) + faa = 0.5*(math.e+1/math.e) trail_frac = 1-0.1*(fy-1)/(faa-1) except Exception as e: print(e) @@ -289,79 +293,96 @@ def BleedingTrail(aa, yy): return trail_frac + def MakeTrail(imgarr, satuyxtuple, charge, fullwell=9e4, direction='up', trailcutfrac=0.9): ''' direction: "up" or "down". For "up", bleeds along Y-decreasing direction; for "down", bleeds along Y-increasing direction. ''' - yi,xi = satuyxtuple + yi, xi = satuyxtuple aa = np.log(charge/fullwell)**3 # scale length of the bleeding trail yy = 1 - while charge>0: - if yi<0 or yi>imgarr.shape[0]-1: + while charge > 0: + if yi < 0 or yi > imgarr.shape[0]-1: break - if yi==0 or yi==imgarr.shape[0]-1: - imgarr[yi,xi] = fullwell + if yi == 0 or yi == imgarr.shape[0]-1: + imgarr[yi, xi] = fullwell break - if direction=='up': - if imgarr[yi-1,xi]>=fullwell: - imgarr[yi,xi] = fullwell - yi-=1 + + if direction == 'up': + if imgarr[yi-1, xi] >= fullwell: + imgarr[yi, xi] = fullwell + yi -= 1 + # [TEST] charge in the middle + if yi == (imgarr.shape[0] // 2 - 1): + break continue - elif direction=='down': - if imgarr[yi+1,xi]>=fullwell: - imgarr[yi,xi] = fullwell - yi+=1 + elif direction == 'down': + if imgarr[yi+1, xi] >= fullwell: + imgarr[yi, xi] = fullwell + yi += 1 + if yi == (imgarr.shape[0] // 2): + break continue - if aa<=1: - while imgarr[yi,xi] >= fullwell: - imgarr[yi,xi] = fullwell - if direction=='up': - imgarr[yi-1,xi] += charge - charge = imgarr[yi-1,xi]-fullwell - yi-=1 - if yi<0: + if aa <= 1: + while imgarr[yi, xi] >= fullwell: + imgarr[yi, xi] = fullwell + if direction == 'up': + imgarr[yi-1, xi] += charge + charge = imgarr[yi-1, xi]-fullwell + yi -= 1 + # if yi < 0: + if yi < 0 or yi == (imgarr.shape[0]//2 - 1): break - elif direction=='down': - imgarr[yi+1,xi] += charge - charge = imgarr[yi+1,xi]-fullwell - yi+=1 - if yi>imgarr.shape[0]: + elif direction == 'down': + imgarr[yi+1, xi] += charge + charge = imgarr[yi+1, xi]-fullwell + yi += 1 + # if yi > imgarr.shape[0]: + if yi > imgarr.shape[0] or yi == (imgarr.shape[0]//2): break else: # calculate bleeding trail: - trail_frac = BleedingTrail(aa,yy) + trail_frac = BleedingTrail(aa, yy) # put charge upwards - if trail_frac>=0.99: - imgarr[yi,xi] = fullwell - if direction=='up': - yi-=1 - elif direction=='down': - yi+=1 + if trail_frac >= 0.99: + imgarr[yi, xi] = fullwell + if direction == 'up': + yi -= 1 + if yi == (imgarr.shape[0]//2 - 1): + break + elif direction == 'down': + yi += 1 + if yi == (imgarr.shape[0]//2): + break yy += 1 else: - if trail_fracfullwell: - imgarr[yi,xi] = fullwell - - if direction=='up': - yi-=1 - elif direction=='down': - yi+=1 + imgarr[yi, xi] += charge + if imgarr[yi, xi] > fullwell: + imgarr[yi, xi] = fullwell + + if direction == 'up': + yi -= 1 + if yi == (imgarr.shape[0]//2 - 1): + break + elif direction == 'down': + yi += 1 + if yi == (imgarr.shape[0]//2): + break yy += 1 return imgarr def ChargeFlow(imgarr, fullwell=9E4): - size_y,size_x = imgarr.shape - satupos_y,satupos_x = np.where(imgarr>fullwell) + size_y, size_x = imgarr.shape + satupos_y, satupos_x = np.where(imgarr > fullwell) - if satupos_y.shape[0]==0: + if satupos_y.shape[0] == 0: # make no change for the image array return imgarr elif satupos_y.shape[0]/imgarr.size > 0.5: @@ -371,28 +392,28 @@ def ChargeFlow(imgarr, fullwell=9E4): chargedict = {} imgarrorig = copy.deepcopy(imgarr) - for yi,xi in zip(satupos_y,satupos_x): - yxidx = ''.join([str(yi),str(xi)]) - chargedict[yxidx] = imgarrorig[yi,xi]-fullwell + for yi, xi in zip(satupos_y, satupos_x): + yxidx = ''.join([str(yi), str(xi)]) + chargedict[yxidx] = imgarrorig[yi, xi]-fullwell - for yi,xi in zip(satupos_y,satupos_x): - yxidx = ''.join([str(yi),str(xi)]) + for yi, xi in zip(satupos_y, satupos_x): + yxidx = ''.join([str(yi), str(xi)]) satcharge = chargedict[yxidx] chargeup = ((np.random.random()-0.5)*0.05+0.5)*satcharge chargedn = satcharge - chargeup try: # Charge Clump moves up - if yi>=0 and yi= 0 and yi < imgarr.shape[0]: + imgarr = MakeTrail(imgarr, (yi, xi), chargeup, fullwell=9e4, direction='up', trailcutfrac=0.9) # Charge Clump moves down - imgarr = MakeTrail(imgarr, (yi,xi), chargedn, fullwell=9e4, direction='down', trailcutfrac=0.9) + imgarr = MakeTrail(imgarr, (yi, xi), chargedn, fullwell=9e4, direction='down', trailcutfrac=0.9) except Exception as e: - print(e,'@pix ',(yi+1,xi+1)) + print(e, '@pix ', (yi+1, xi+1)) return imgarr - return imgarr + def SaturBloom(GSImage, nsect_x=1, nsect_y=1, fullwell=9e4): """ To simulate digital detector's saturation and blooming effect. The blooming is along the read-out direction, perpendicular to the charge transfer direction. Charge clumpy overflows the pixel well will flow to two oposite directions with nearly same charges. @@ -418,42 +439,42 @@ def SaturBloom(GSImage, nsect_x=1, nsect_y=1, fullwell=9e4): return GSImage -################################# Saturation & Bleeding End #################################### +# Saturation & Bleeding End # def readout16(GSImage, rowi=0, coli=0, overscan_value=0): # readout image as 16 outputs of sub-images plus prescan & overscan. # assuming image width and height sizes are both even. - # assuming image has 2 columns and 8 rows of output channels. + # assuming image has 2 columns and 8 rows of output channels. # 00 01 # 10 11 # 20 21 # ... # return: GS Image Object - npix_y,npix_x = GSImage.array.shape + npix_y, npix_x = GSImage.array.shape subheight = int(8+npix_y/2+8) subwidth = int(16+npix_x/8+27) OutputSubimg = galsim.ImageUS(subwidth, subheight, init_value=overscan_value) - if rowi<4 and coli==0: + if rowi < 4 and coli == 0: subbounds = galsim.BoundsI(1, int(npix_x/2), int(npix_y/8*rowi+1), int(npix_y/8*(rowi+1))) subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1)) subimg = GSImage[subbounds] - OutputSubimg.array[27:int(npix_y/8)+27,8:int(npix_x/2)+8] = subimg.array - elif rowi<4 and coli==1: + OutputSubimg.array[27:int(npix_y/8)+27, 8:int(npix_x/2)+8] = subimg.array + elif rowi < 4 and coli == 1: subbounds = galsim.BoundsI(npix_x/2+1, npix_x, npix_y/8*rowi+1, npix_y/8*(rowi+1)) subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1)) subimg = GSImage[subbounds] - OutputSubimg.array[27:int(npix_y/8)+27,8:int(npix_x/2)+8] = subimg.array - elif rowi>=4 and rowi<8 and coli==0: + OutputSubimg.array[27:int(npix_y/8)+27, 8:int(npix_x/2)+8] = subimg.array + elif rowi >= 4 and rowi < 8 and coli == 0: subbounds = galsim.BoundsI(1, npix_x/2, npix_y/8*rowi+1, npix_y/8*(rowi+1)) subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1)) subimg = GSImage[subbounds] - OutputSubimg.array[16:int(npix_y/8)+16,8:int(npix_x/2)+8] = subimg.array - elif rowi>=4 and rowi<8 and coli==1: + OutputSubimg.array[16:int(npix_y/8)+16, 8:int(npix_x/2)+8] = subimg.array + elif rowi >= 4 and rowi < 8 and coli == 1: subbounds = galsim.BoundsI(npix_x/2+1, npix_x, npix_y/8*rowi+1, npix_y/8*(rowi+1)) subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1)) subimg = GSImage[subbounds] - OutputSubimg.array[16 :int(npix_y/8)+16,8:int(npix_x/2)+8] = subimg.array + OutputSubimg.array[16:int(npix_y/8)+16, 8:int(npix_x/2)+8] = subimg.array else: print("\n\033[31mError: "+"Wrong rowi or coli assignment. Permitted: 0<=rowi<=7, 0<=coli<=1."+"\033[0m\n") return OutputSubimg @@ -463,169 +484,163 @@ def readout16(GSImage, rowi=0, coli=0, overscan_value=0): def CTE_Effect(GSImage, threshold=27, direction='column'): # Devide the image into 4 sections and apply CTE effect with different trail directions. # GSImage: a GalSim Image object. - size_y,size_x = GSImage.array.shape + size_y, size_x = GSImage.array.shape size_sect_y = int(size_y/2) size_sect_x = int(size_x/2) imgarr = GSImage.array if direction == 'column': - imgarr[0:size_sect_y,:] = CTEModelColRow(imgarr[0:size_sect_y,:], trail_direction='down', direction='column', threshold=threshold) - imgarr[size_sect_y:size_y,:] = CTEModelColRow(imgarr[size_sect_y:size_y,:], trail_direction='up', direction='column', threshold=threshold) + imgarr[0:size_sect_y, :] = CTEModelColRow(imgarr[0:size_sect_y, :], trail_direction='down', direction='column', threshold=threshold) + imgarr[size_sect_y:size_y, :] = CTEModelColRow(imgarr[size_sect_y:size_y, :], trail_direction='up', direction='column', threshold=threshold) elif direction == 'row': - imgarr[:,0:size_sect_x] = CTEModelColRow(imgarr[:,0:size_sect_x], trail_direction='right', direction='row', threshold=threshold) - imgarr[:,size_sect_x:size_x] = CTEModelColRow(imgarr[:,size_sect_x:size_x], trail_direction='left', direction='row', threshold=threshold) + imgarr[:, 0:size_sect_x] = CTEModelColRow(imgarr[:, 0:size_sect_x], trail_direction='right', direction='row', threshold=threshold) + imgarr[:, size_sect_x:size_x] = CTEModelColRow(imgarr[:, size_sect_x:size_x], trail_direction='left', direction='row', threshold=threshold) return GSImage @jit() -def CTEModelColRow(img, trail_direction = 'up', direction='column', threshold=27): +def CTEModelColRow(img, trail_direction='up', direction='column', threshold=27): - #total trail flux vs (pixel flux)^1/2 is approximately linear - #total trail flux = trail_a * (pixel flux)^1/2 + trail_b - #trail pixel flux = pow(0.5,x)/0.5, normalize to 1 - trail_a = 5.651803799619966 + # total trail flux vs (pixel flux)^1/2 is approximately linear + # total trail flux = trail_a * (pixel flux)^1/2 + trail_b + # trail pixel flux = pow(0.5,x)/0.5, normalize to 1 + trail_a = 5.651803799619966 trail_b = -2.671933068990729 sh1 = img.shape[0] sh2 = img.shape[1] n_img = img*0 - idx = np.where(img0: + elif len(idx[0]) > 0: n_img[idx] = img[idx] - yidx,xidx = np.where(img>=threshold) + yidx, xidx = np.where(img >= threshold) if len(yidx) == 0: pass - elif len(yidx)>0: + elif len(yidx) > 0: # print(index) - for i, j in zip(yidx,xidx): - f = img[i,j] + for i, j in zip(yidx, xidx): + f = img[i, j] trail_f = (np.sqrt(f)*trail_a + trail_b)*0.5 # trail_f=5E-5*f**1.5 xy_num = 10 all_trail = np.zeros(xy_num) - - xy_upstr = np.arange(1,xy_num,1) + + xy_upstr = np.arange(1, xy_num, 1) # all_trail_pix = np.sum(pow(0.5,xy_upstr)/0.5) all_trail_pix = 0 for m in xy_upstr: - a1=12.97059491 - b1=0.54286652 - c1=0.69093105 - a2=2.77298856 - b2=0.11231055 - c2=-0.01038675 + a1 = 12.97059491 + b1 = 0.54286652 + c1 = 0.69093105 + a2 = 2.77298856 + b2 = 0.11231055 + c2 = -0.01038675 # t_pow = 0 - am=1 - bm=1 + am = 1 + bm = 1 t_pow = am*np.exp(-bm*m) # if m < 5: # t_pow = a1*np.exp(-b1*m)+c1 # else: # t_pow = a2*np.exp(-b2*m)+c2 - if t_pow <0: + if t_pow < 0: t_pow = 0 all_trail_pix += t_pow all_trail[m] = t_pow - - trail_pix_eff = trail_f/all_trail_pix - all_trail = trail_pix_eff*all_trail - all_trail[0] = f - trail_f - - for m in np.arange(0,xy_num,1): + + for m in np.arange(0, xy_num, 1): if direction == 'column': if trail_direction == 'down': y_pos = i + m elif trail_direction == 'up': y_pos = i - m - if y_pos < 0 or y_pos >=sh1: + if y_pos < 0 or y_pos >= sh1: break - n_img[y_pos,j] = n_img[y_pos,j] + all_trail[m] + n_img[y_pos, j] = n_img[y_pos, j] + all_trail[m] elif direction == 'row': if trail_direction == 'left': x_pos = j - m elif trail_direction == 'right': x_pos = j + m - if x_pos < 0 or x_pos >=sh2: + if x_pos < 0 or x_pos >= sh2: break - n_img[i,x_pos] = n_img[i,x_pos] + all_trail[m] + n_img[i, x_pos] = n_img[i, x_pos] + all_trail[m] return n_img - -#---------- For Cosmic-Ray Simulation ------------ -#---------- Zhang Xin ---------------------------- - +# ---------- For Cosmic-Ray Simulation ------------ +# ---------- Zhang Xin ---------------------------- def getYValue(collection, x): - index = 0; + index = 0 if (collection.shape[1] == 2): - while(x>collection[index,0] and index < collection.shape[0]): - index= index + 1; + while (x > collection[index, 0] and index < collection.shape[0]): + index = index + 1 if (index == collection.shape[0] or index == 0): - return 0; + return 0 - deltX = collection[index, 0] - collection[index-1, 0]; - deltY = collection[index, 1] - collection[index-1, 1]; + deltX = collection[index, 0] - collection[index-1, 0] + deltY = collection[index, 1] - collection[index-1, 1] if deltX == 0: return (collection[index, 1] + collection[index-1, 1])/2.0 else: - a = deltY/deltX; - return a * (x - collection[index-1,0]) + collection[index-1, 1]; - return 0; + a = deltY/deltX + return a * (x - collection[index-1, 0]) + collection[index-1, 1] + return 0 -def selectCosmicRayCollection(attachedSizes, xLen, yLen,cr_pixelRatio,CR_max_size): +def selectCosmicRayCollection(attachedSizes, xLen, yLen, cr_pixelRatio, CR_max_size): normalRay = 0.90 nnormalRay = 1-normalRay max_nrayLen = 100 - pixelNum = int(xLen * yLen * cr_pixelRatio * normalRay ); - pixelNum_n = int(xLen * yLen * cr_pixelRatio * nnormalRay ) - CRPixelNum = 0; - - maxValue = max(attachedSizes[:,1]) - maxValue += 0.1; - - cr_event_num = 0; - CRs = np.zeros(pixelNum); + pixelNum = int(xLen * yLen * cr_pixelRatio * normalRay) + pixelNum_n = int(xLen * yLen * cr_pixelRatio * nnormalRay) + CRPixelNum = 0 + + maxValue = max(attachedSizes[:, 1]) + maxValue += 0.1 + + cr_event_num = 0 + CRs = np.zeros(pixelNum) while (CRPixelNum < pixelNum): - x = CR_max_size * np.random.random(); - y = maxValue * np.random.random(); + x = CR_max_size * np.random.random() + y = maxValue * np.random.random() if (y <= getYValue(attachedSizes, x)): - CRs[cr_event_num] = np.ceil(x); - cr_event_num = cr_event_num + 1; - CRPixelNum = CRPixelNum + round(x); + CRs[cr_event_num] = np.ceil(x) + cr_event_num = cr_event_num + 1 + CRPixelNum = CRPixelNum + round(x) while (CRPixelNum < pixelNum + pixelNum_n): nx = np.random.random()*(max_nrayLen-CR_max_size)+CR_max_size - CRs[cr_event_num] = np.ceil(nx); - cr_event_num = cr_event_num + 1; - CRPixelNum = CRPixelNum + np.ceil(nx); + CRs[cr_event_num] = np.ceil(nx) + cr_event_num = cr_event_num + 1 + CRPixelNum = CRPixelNum + np.ceil(nx) - return CRs[0:cr_event_num]; + return CRs[0:cr_event_num] -def defineEnergyForCR(cr_event_size,seed = 12345): +def defineEnergyForCR(cr_event_size, seed=12345): import random - sigma = 0.6 / 2.355; - mean = 3.3; + sigma = 0.6 / 2.355 + mean = 3.3 random.seed(seed) - energys = np.zeros(cr_event_size); + energys = np.zeros(cr_event_size) for i in np.arange(cr_event_size): - energy_index = random.normalvariate(mean,sigma); - energys[i] = pow(10, energy_index); + energy_index = random.normalvariate(mean, sigma) + energys[i] = pow(10, energy_index) + return energys - return energys; -def convCR(CRmap=None, addPSF=None, sp_n = 4): +def convCR(CRmap=None, addPSF=None, sp_n=4): sh = CRmap.shape # sp_n = 4 @@ -634,23 +649,23 @@ def convCR(CRmap=None, addPSF=None, sp_n = 4): for i in np.arange(sh[0]): i_st = sp_n*i for j in np.arange(sh[1]): - if CRmap[i,j] ==0: + if CRmap[i, j] == 0: continue j_st = sp_n*j - pix_v1 = CRmap[i,j]*pix_v0 + pix_v1 = CRmap[i, j]*pix_v0 for m in np.arange(sp_n): for n in np.arange(sp_n): subCRmap[i_st+m, j_st + n] = pix_v1 m_size = addPSF.shape[0] - subCRmap_n = np.zeros(np.array(subCRmap.shape) + m_size -1) + subCRmap_n = np.zeros(np.array(subCRmap.shape) + m_size - 1) for i in np.arange(subCRmap.shape[0]): for j in np.arange(subCRmap.shape[1]): - if subCRmap[i,j]>0: - convPix = addPSF*subCRmap[i,j] - subCRmap_n[i:i+m_size,j:j+m_size]+=convPix + if subCRmap[i, j] > 0: + convPix = addPSF*subCRmap[i, j] + subCRmap_n[i:i+m_size, j:j+m_size] += convPix CRmap_n = np.zeros((np.array(subCRmap_n.shape)/sp_n).astype(np.int32)) sh_n = CRmap_n.shape @@ -659,12 +674,12 @@ def convCR(CRmap=None, addPSF=None, sp_n = 4): i_st = sp_n*i for j in np.arange(sh_n[1]): p_v = 0 - j_st=sp_n*j + j_st = sp_n*j for m in np.arange(sp_n): for n in np.arange(sp_n): p_v += subCRmap_n[i_st+m, j_st + n] - CRmap_n[i,j] = p_v + CRmap_n[i, j] = p_v return CRmap_n @@ -673,15 +688,15 @@ def produceCR_Map(xLen, yLen, exTime, cr_pixelRatio, gain, attachedSizes, seed=2 # Return: an 2-D numpy array # attachedSizes = np.loadtxt('./wfc-cr-attachpixel.dat'); np.random.seed(seed) - CR_max_size = 20.0; - cr_size = selectCosmicRayCollection(attachedSizes, xLen, yLen, cr_pixelRatio, CR_max_size); + CR_max_size = 20.0 + cr_size = selectCosmicRayCollection(attachedSizes, xLen, yLen, cr_pixelRatio, CR_max_size) - cr_event_size = cr_size.shape[0]; - cr_energys = defineEnergyForCR(cr_event_size,seed = seed); + cr_event_size = cr_size.shape[0] + cr_energys = defineEnergyForCR(cr_event_size, seed=seed) - CRmap = np.zeros([yLen, xLen]); + CRmap = np.zeros([yLen, xLen]) - ## produce conv kernel + # produce conv kernel from astropy.modeling.models import Gaussian2D o_size = 4 sp_n = 8 @@ -694,28 +709,26 @@ def produceCR_Map(xLen, yLen, exTime, cr_pixelRatio, gain, attachedSizes, seed=2 addPSF = addPSF_(xp, yp) convKernel = addPSF/addPSF.sum() - ################################# - - + # --------------------------------- for i in np.arange(cr_event_size): - xPos = round((xLen - 1)* np.random.random()); - yPos = round((yLen - 1)* np.random.random()); - cr_lens = int(cr_size[i]); - if cr_lens ==0: - continue; - pix_energy = cr_energys[i]/gain/cr_lens; - pos_angle = 1/2*math.pi*np.random.random(); + xPos = round((xLen - 1) * np.random.random()) + yPos = round((yLen - 1) * np.random.random()) + cr_lens = int(cr_size[i]) + if cr_lens == 0: + continue + pix_energy = cr_energys[i]/gain/cr_lens + pos_angle = 1/2*math.pi*np.random.random() crMatrix = np.zeros([cr_lens+1, cr_lens + 1]) for j in np.arange(cr_lens): - x_n = int(np.cos(pos_angle)*j - np.sin(pos_angle)*0); + x_n = int(np.cos(pos_angle)*j - np.sin(pos_angle)*0) if x_n < 0: x_n = x_n + cr_lens+1 - y_n = int(np.sin(pos_angle)*j + np.cos(pos_angle)*0); - if x_n<0 or x_n >cr_lens or y_n < 0 or y_n > cr_lens: - continue; - crMatrix[y_n,x_n] = pix_energy; + y_n = int(np.sin(pos_angle)*j + np.cos(pos_angle)*0) + if x_n < 0 or x_n > cr_lens or y_n < 0 or y_n > cr_lens: + continue + crMatrix[y_n, x_n] = pix_energy crMatrix_n = convCR(crMatrix, convKernel, sp_n) # crMatrix_n = crMatrix @@ -729,9 +742,7 @@ def produceCR_Map(xLen, yLen, exTime, cr_pixelRatio, gain, attachedSizes, seed=2 sly = slice(ypix[oky].min(), ypix[oky].max()+1) slx = slice(xpix[okx].min(), xpix[okx].max()+1) - CRmap[sly, slx] += crMatrix_n[oky,:][:,okx] - - + CRmap[sly, slx] += crMatrix_n[oky, :][:, okx] return CRmap.astype(np.int32), cr_event_size @@ -754,7 +765,7 @@ def ShutterEffectArr(GSImage, t_exp=150, t_shutter=1.3, dist_bearing=735, dt=1E- s = np.zeros(SampleNumb) s1 = np.zeros(SampleNumb) s2 = np.zeros(SampleNumb) - brt = np.zeros(SampleNumb) + brt = np.zeros(SampleNumb) idx = np.arange(SampleNumb) sidx = np.zeros(SampleNumb) s1idx = np.zeros(SampleNumb) @@ -770,9 +781,9 @@ def ShutterEffectArr(GSImage, t_exp=150, t_shutter=1.3, dist_bearing=735, dt=1E- s2[i] = dist_bearing-DistHalf*np.cos(theta[i]) s1idx[i] = int(s1[i]/dist_bearing*(SampleNumb)) s2idx[i] = int(s2[i]/dist_bearing*(SampleNumb)) - brt[(idx>s1idx[i]) & (idx s1idx[i]) & (idx < s2idx[i])] += dt - if t_exp>t_shutter*2: + if t_exp > t_shutter*2: brt = brt*2+(t_exp-t_shutter*2) else: brt = brt*2 @@ -785,16 +796,16 @@ def ShutterEffectArr(GSImage, t_exp=150, t_shutter=1.3, dist_bearing=735, dt=1E- xmax = GSImage.bounds.getXMax() ymin = GSImage.bounds.getYMin() ymax = GSImage.bounds.getYMax() - if xminnp.max(x): + if xmin < np.min(x) or xmax > np.max(x): raise LookupError("Out of focal-plane bounds in X-direction.") - if ymin<-25331 or ymax>25331: + if ymin < -25331 or ymax > 25331: raise LookupError("Out of focal-plane bounds in Y-direction.") sizex = xmax-xmin+1 sizey = ymax-ymin+1 xnewgrid = np.mgrid[xmin:(xmin+sizex)] expeffect = interpolate.splev(xnewgrid, intp, der=0) expeffect /= t_exp - exparrnormal = np.tile(expeffect, (sizey,1)) + exparrnormal = np.tile(expeffect, (sizey, 1)) # GSImage *= exparrnormal return exparrnormal diff --git a/observation_sim/instruments/chip/libBF/diffusion_X1.c b/observation_sim/instruments/chip/libBF/diffusion_X1.c index 199b48cc94540419cc206dbf327b40aa13b2f849..b0259e8169130873d1bd3a2ec3fdcd08cf1fcdda 100644 --- a/observation_sim/instruments/chip/libBF/diffusion_X1.c +++ b/observation_sim/instruments/chip/libBF/diffusion_X1.c @@ -37,7 +37,7 @@ void addEffects(int ngx_ima, int ngy_ima, float *arr_ima, float *arr_imc, int bi { printf("Adding BF effect...\n"); //setup BF correlation fliter - float neX; + float neX, neXtemp; float neP1 = 50000; float bfaP1[9]={0.9707182, 0.002143905, 0.004131103, 0.001149542, 0.0005501739, 0.0005469659, 0.0003726081, 0.0003795207, 0.0001633302}; float neP2 = 10000; @@ -56,15 +56,18 @@ void addEffects(int ngx_ima, int ngy_ima, float *arr_ima, float *arr_imc, int bi neX = arr_ima[j+i*ny]; if(neX >= 10000) { + neXtemp = neX; + if(neXtemp > 100000) + neXtemp = 100000; bfa[0][0]=0; //linearInterp(neX, neP1, bfaP1[0], neP2, bfaP2[0]); //0; - bfa[0][1]=bfa[0][-1]=linearInterp(neX, neP1, bfaP1[1], neP2, bfaP2[1]); //0.01575; - bfa[-1][0]=bfa[1][0]=linearInterp(neX, neP1, bfaP1[2], neP2, bfaP2[2]); //0.00652; - bfa[-1][-1]=bfa[1][1]=bfa[-1][1]=bfa[1][-1]=linearInterp(neX, neP1, bfaP1[3], neP2, bfaP2[3]); //0.00335; - bfa[0][-2]=bfa[0][2]=linearInterp(neX, neP1, bfaP1[4], neP2, bfaP2[4]); - bfa[-2][0]=bfa[2][0]=linearInterp(neX, neP1, bfaP1[5], neP2, bfaP2[5]); //0.00118; - bfa[-2][-1]=bfa[-2][1]=bfa[2][1]=bfa[2][-1]=linearInterp(neX, neP1, bfaP1[6], neP2, bfaP2[6]); - bfa[-1][-2]=bfa[1][2]=bfa[-1][2]=bfa[1][-2]=linearInterp(neX, neP1, bfaP1[7], neP2, bfaP2[7]); //0.00083; - bfa[-2][-2]=bfa[-2][2]=bfa[2][-2]=bfa[2][2]=linearInterp(neX, neP1, bfaP1[8], neP2, bfaP2[8]); //0.00043; + bfa[0][1]=bfa[0][-1]=linearInterp(neXtemp, neP1, bfaP1[1], neP2, bfaP2[1]); //0.01575; + bfa[-1][0]=bfa[1][0]=linearInterp(neXtemp, neP1, bfaP1[2], neP2, bfaP2[2]); //0.00652; + bfa[-1][-1]=bfa[1][1]=bfa[-1][1]=bfa[1][-1]=linearInterp(neXtemp, neP1, bfaP1[3], neP2, bfaP2[3]); //0.00335; + bfa[0][-2]=bfa[0][2]=linearInterp(neXtemp, neP1, bfaP1[4], neP2, bfaP2[4]); + bfa[-2][0]=bfa[2][0]=linearInterp(neXtemp, neP1, bfaP1[5], neP2, bfaP2[5]); //0.00118; + bfa[-2][-1]=bfa[-2][1]=bfa[2][1]=bfa[2][-1]=linearInterp(neXtemp, neP1, bfaP1[6], neP2, bfaP2[6]); + bfa[-1][-2]=bfa[1][2]=bfa[-1][2]=bfa[1][-2]=linearInterp(neXtemp, neP1, bfaP1[7], neP2, bfaP2[7]); //0.00083; + bfa[-2][-2]=bfa[-2][2]=bfa[2][-2]=bfa[2][2]=linearInterp(neXtemp, neP1, bfaP1[8], neP2, bfaP2[8]); //0.00043; } else { diff --git a/observation_sim/instruments/chip/libCTI/CTI_modeling.py b/observation_sim/instruments/chip/libCTI/CTI_modeling.py index 6c840599d15fc24a779f554b4abd05527017bc84..fdf8b41a99c544f291f6970083ffaca0c9b49849 100644 --- a/observation_sim/instruments/chip/libCTI/CTI_modeling.py +++ b/observation_sim/instruments/chip/libCTI/CTI_modeling.py @@ -31,7 +31,7 @@ def get_trap_map(seeds,nx,ny,nmax,rho_trap,beta,c,out_dir): get_trap_h(seeds_p,c_int(int(nsp)),c_int(int(nx)),c_int(int(ny)),\ c_int(int(nmax)),rho_trap_p,c_float(beta),\ c_float(c),filename) - + def bin2fits(bin_file,fits_dir,nsp,nx,ny,nmax): data = np.fromfile(bin_file,dtype=np.float32) data = data.reshape(nx,nsp,ny,nmax).transpose(1,3,2,0) @@ -101,6 +101,6 @@ if __name__ =='__main__': image = fits.getdata("inputdata/image.fits").astype(np.int32) get_trap_map(trap_seeds,nx,ny,nmax,rho_trap,beta,c,".") bin2fits("trap.bin",".",nsp,nx,ny,nmax) - image_cti = CTI_sim(image,nx,ny,noverscan,nsp,nmax,beta,w,c,t,rho_trap,trap_seeds,release_seed) + image_cti = CTI_sim(image,nx,ny,noverscan,nsp,nmax,beta,w,c,t,rho_trap,trap_seeds,release_seed) fits.writeto("output/image_CTI.fits",data=image_cti,overwrite=True) """ diff --git a/observation_sim/instruments/data/sls_conf/._CSST_GI1.conf b/observation_sim/instruments/data/sls_conf/._CSST_GI1.conf deleted file mode 100755 index afe84e13b241509cf960068981c9e7033f876e86..0000000000000000000000000000000000000000 Binary files a/observation_sim/instruments/data/sls_conf/._CSST_GI1.conf and /dev/null differ diff --git a/observation_sim/instruments/data/sls_conf/._CSST_GV1.conf b/observation_sim/instruments/data/sls_conf/._CSST_GV1.conf deleted file mode 100755 index 13acd34cf4136ed3b41e0ff98d97019deda0d631..0000000000000000000000000000000000000000 Binary files a/observation_sim/instruments/data/sls_conf/._CSST_GV1.conf and /dev/null differ diff --git a/observation_sim/mock_objects/CatalogBase.py b/observation_sim/mock_objects/CatalogBase.py index cd1b669997ed0ac00b27c991885898e283c87714..e3896a00f118b944a6fadea81cae969f3689e316 100644 --- a/observation_sim/mock_objects/CatalogBase.py +++ b/observation_sim/mock_objects/CatalogBase.py @@ -12,6 +12,9 @@ class CatalogBase(metaclass=ABCMeta): def __init__(self): pass + def free_mem(self, **kward): + pass + @abstractmethod def load_sed(self, obj, **kward): pass @@ -29,8 +32,8 @@ class CatalogBase(metaclass=ABCMeta): param = { "star": -1, "id": -1, - "ra": 0, - "dec": 0., + "ra": -999., + "dec": -999., "ra_orig": 0., "dec_orig": 0., "z": 0., @@ -44,15 +47,15 @@ class CatalogBase(metaclass=ABCMeta): "bfrac": 0., "av": 0., "redden": 0., - "hlr_bulge": 0., - "hlr_disk": 0., + "hlr_bulge": -999., + "hlr_disk": -999., "ell_bulge": 0., "ell_disk": 0., "ell_tot": 0., - "e1_disk": 0., - "e2_disk": 0., - "e1_bulge": 0., - "e2_bulge": 0., + "e1_disk": -999., + "e2_disk": -999., + "e1_bulge": -999., + "e2_bulge": -999., "teff": 0., "logg": 0., "feh": 0., @@ -91,6 +94,8 @@ class CatalogBase(metaclass=ABCMeta): bandpass = target_filt.bandpass_full if norm_filt is not None: + if hasattr(norm_filt, 'bandpass_full'): + norm_filt = Table(np.array(np.array([norm_filt.bandpass_full.wave_list*10.0, norm_filt.bandpass_full.func(norm_filt.bandpass_full.wave_list)])).T, names=(['WAVELENGTH', 'SENSITIVITY'])) norm_thr_rang_ids = norm_filt['SENSITIVITY'] > 0.001 else: norm_filt = Table( diff --git a/observation_sim/mock_objects/FlatLED.py b/observation_sim/mock_objects/FlatLED.py index a99e955c95401f8aafe52841cfe9d10633cb0125..2674aa5c6ff8659db1c06a96e070d74db9c61d7d 100755 --- a/observation_sim/mock_objects/FlatLED.py +++ b/observation_sim/mock_objects/FlatLED.py @@ -50,6 +50,8 @@ fluxLED = {'LED1': 15, 'LED2': 15, 'LED3': 12.5, 'LED4': 9, 'LED5': 9, # 'LED14': 10} mirro_eff = {'GU': 0.61, 'GV': 0.8, 'GI': 0.8} + +bandtoLed = {'NUV': ['LED1', 'LED2'], 'u': ['LED13', 'LED14'], 'g': ['LED3', 'LED4', 'LED5'], 'r': ['LED6', 'LED7'], 'i': ['LED8'], 'z': ['LED9', 'LED10'], 'y': ['LED10'], 'GU': ['LED1', 'LED2', 'LED13', 'LED14'], 'GV': ['LED3', 'LED4', 'LED5', 'LED6'], 'GI': ['LED7', 'LED8', 'LED9', 'LED10']} # mirro_eff = {'GU':1, 'GV':1, 'GI':1} @@ -69,10 +71,18 @@ class FlatLED(MockObject): with pkg_resources.path('observation_sim.mock_objects.data.led', "") as ledDir: self.flatDir = ledDir.as_posix() + def getInnerFlat(self): + ledflats = bandtoLed[self.chip.filter_type] + iFlat = np.zeros([self.chip.npix_y, self.chip.npix_x]) + for nled in ledflats: + iFlat = iFlat + self.getLEDImage1(led_type=nled, LED_Img_flag=False) + iFlat = iFlat/len(ledflats) + return iFlat + ### # return LED flat, e/s ### - def getLEDImage(self, led_type='LED1'): + def getLEDImage(self, led_type='LED1', LED_Img_flag=True): # cwave = cwaves[led_type] flat = fits.open(os.path.join(self.flatDir, 'model_' + cwaves_name[led_type] + 'nm.fits')) @@ -102,7 +112,10 @@ class FlatLED(MockObject): N[self.chip.npix_y * i:self.chip.npix_y * (i + 1), self.chip.npix_x * j:self.chip.npix_x * (j + 1)]), method='linear') U = U/np.mean(U) - flatImage = U*fluxLED[led_type]*1000 + + flatImage = U + if LED_Img_flag: + flatImage = flatImage*fluxLED[led_type]*1000 gc.collect() return flatImage @@ -110,39 +123,42 @@ class FlatLED(MockObject): # return LED flat, e/s ### - def getLEDImage1(self, led_type='LED1'): + def getLEDImage1(self, led_type='LED1', LED_Img_flag=True): # cwave = cwaves[led_type] flat = fits.open(os.path.join(self.flatDir, 'model_' + cwaves_name[led_type] + 'nm.fits')) xlen = flat[0].header['NAXIS1'] ylen = 601 - i = self.chip.rowID - 1 j = self.chip.colID - 1 - x = np.linspace(0, self.chip.npix_x, int(xlen/6.)) y = np.linspace(0, self.chip.npix_y, int(ylen/5.)) xx, yy = np.meshgrid(x, y) - a1 = flat[0].data[int(ylen*i/5.):int(ylen*i/5.)+int(ylen/5.), int(xlen*j/6.):int(xlen*j/6.)+int(xlen/6.)] # z = np.sin((xx+yy+xx**2+yy**2)) # fInterp = interp2d(xx, yy, z, kind='linear') - X_ = np.hstack((xx.flatten()[:, None], yy.flatten()[:, None])) Z_ = a1.flatten() - n_x = np.arange(0, self.chip.npix_x, 1) n_y = np.arange(0, self.chip.npix_y, 1) - M, N = np.meshgrid(n_x, n_y) - - U = griddata(X_, Z_, ( - M[0:self.chip.npix_y, 0:self.chip.npix_x], - N[0:self.chip.npix_y, 0:self.chip.npix_x]), - method='linear') + x_seg_len = 4 + y_seg_len = 8 + x_seg = int(self.chip.npix_x/x_seg_len) + y_seg = int(self.chip.npix_y/y_seg_len) + U = np.zeros([self.chip.npix_y, self.chip.npix_x], dtype=np.float32) + for y_seg_i in np.arange(y_seg_len): + for x_seg_i in np.arange(x_seg_len): + U[y_seg_i*y_seg:(y_seg_i+1)*y_seg, x_seg_i*x_seg:(x_seg_i+1)*x_seg] = griddata(X_, Z_, (M[y_seg_i*y_seg:(y_seg_i+1)*y_seg, x_seg_i*x_seg:(x_seg_i+1)*x_seg], N[y_seg_i*y_seg:(y_seg_i+1)*y_seg, x_seg_i*x_seg:(x_seg_i+1)*x_seg]), method='linear') + # U = griddata(X_, Z_, ( + # M[0:self.chip.npix_y, 0:self.chip.npix_x], + # N[0:self.chip.npix_y, 0:self.chip.npix_x]), + # method='nearest').astype(np.float32) U = U/np.mean(U) - flatImage = U*fluxLED[led_type]*1000 + flatImage = U + if LED_Img_flag: + flatImage = U*fluxLED[led_type]*1000 gc.collect() return flatImage diff --git a/observation_sim/mock_objects/Galaxy.py b/observation_sim/mock_objects/Galaxy.py index 8dc9772633d2ee61f90776cb8344b81fa308f5d7..97df60cdf82ff9ad353ce95db490ba72a593d877 100755 --- a/observation_sim/mock_objects/Galaxy.py +++ b/observation_sim/mock_objects/Galaxy.py @@ -33,7 +33,7 @@ class Galaxy(MockObject): raise ValueError( "!!!The number of PSF profiles and the number of bandpasses must be equal.") objs = [] - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) # print("nphotons_tot = ", nphotons_tot) @@ -92,7 +92,7 @@ class Galaxy(MockObject): return final def drawObj_multiband(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0, exptime=150., fd_shear=None): - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) # print("nphotons_tot = ", nphotons_tot) @@ -115,7 +115,7 @@ class Galaxy(MockObject): # Set Galsim Parameters if self.getMagFilter(filt) <= 15 and (not big_galaxy): - folding_threshold = 5.e-4 + folding_threshold = 5.e-8 else: folding_threshold = 5.e-3 gsp = galsim.GSParams(folding_threshold=folding_threshold) @@ -170,8 +170,11 @@ class Galaxy(MockObject): # print("nphotons_sub-band_%d = %.2f"%(i, nphotons)) # Get PSF model + EXTRA = False + if self.getMagFilter(filt) <= filt.mag_saturation-1.: + EXTRA = True psf, pos_shear = psf_model.get_PSF( - chip=chip, pos_img=pos_img, bandpass=bandpass, folding_threshold=folding_threshold) + chip=chip, pos_img=pos_img, bandpass=bandpass, folding_threshold=folding_threshold, extrapolate=EXTRA) if self.bfrac == 0: gal_temp = disk @@ -325,23 +328,28 @@ class Galaxy(MockObject): galImg_List = [] try: - pos_img_local = [0,0] + pos_img_local = [0, 0] x_start = chip.x_cen/chip.pix_size - chip.npix_x / 2. y_start = chip.y_cen/chip.pix_size - chip.npix_y / 2. pos_img_local[0] = pos_img.x - x_start pos_img_local[1] = pos_img.y - y_start nnx = 0 nny = 0 - for order in ["A","B"]: + for order in ["A", "B"]: + EXTRA = False + if self.getMagFilter(filt) <= filt.mag_saturation-2.: + EXTRA = True psf, pos_shear = psf_model.get_PSF( - chip, pos_img_local=pos_img_local, bandNo=i+1, galsimGSObject=True, g_order=order, grating_split_pos=grating_split_pos) + chip, pos_img_local=pos_img_local, bandNo=i+1, galsimGSObject=True, g_order=order, grating_split_pos=grating_split_pos, extrapolate=EXTRA, ngg=3072) star_p = galsim.Convolve(psf, gal) if nnx == 0: - galImg = star_p.drawImage(wcs=chip_wcs_local, offset=offset) + galImg = star_p.drawImage( + wcs=chip_wcs_local, offset=offset, method='no_pixel') nnx = galImg.xmax - galImg.xmin + 1 nny = galImg.ymax - galImg.ymin + 1 else: - galImg = star_p.drawImage(nx = nnx, ny = nny, wcs=chip_wcs_local, offset=offset) + galImg = star_p.drawImage( + nx=nnx, ny=nny, wcs=chip_wcs_local, offset=offset, method='no_pixel') galImg.setOrigin(0, 0) # n1 = np.sum(np.isinf(galImg.array)) # n2 = np.sum(np.isnan(galImg.array)) @@ -351,19 +359,23 @@ class Galaxy(MockObject): # ERROR happens return 2, pos_shear galImg_List.append(galImg) - for order in ["C","D","E"]: + for order in ["C", "D", "E"]: galImg_List.append(galImg) except: - psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img) - star_p = galsim.Convolve(psf, gal) - galImg = star_p.drawImage(wcs=chip_wcs_local, offset=offset) - galImg.setOrigin(0, 0) - if np.sum(np.isnan(galImg.array)) > 0: - # ERROR happens - return 2, pos_shear - for order in ["A","B","C","D","E"]: - galImg_List.append(galImg) - + try: + psf, pos_shear = psf_model.get_PSF( + chip=chip, pos_img=pos_img) + star_p = galsim.Convolve(psf, gal) + galImg = star_p.drawImage( + wcs=chip_wcs_local, offset=offset) + galImg.setOrigin(0, 0) + if np.sum(np.isnan(galImg.array)) > 0: + # ERROR happens + return 2, pos_shear + for order in ["A", "B", "C", "D", "E"]: + galImg_List.append(galImg) + except Exception as e: + continue # starImg = gal.drawImage( # wcs=chip_wcs_local, offset=offset, method='real_space') @@ -378,7 +390,7 @@ class Galaxy(MockObject): subSlitPos = int(grating_split_pos_chip - gal_origin[1]) # part img disperse - star_p1s=[] + star_p1s = [] for galImg in galImg_List: subImg_p1 = galImg.array[:, 0:subSlitPos] @@ -397,17 +409,18 @@ class Galaxy(MockObject): isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp_p1, chip=chip, xOrderSigPlus = xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp_p1, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp_p1, chip=chip, pos_img_local=[xcenter_p1, ycenter_p1], # psf_model=psf_model, bandNo=i + 1, # grating_split_pos=grating_split_pos, # local_wcs=chip_wcs_local, pos_img=pos_img) - star_p2s=[] + star_p2s = [] for galImg in galImg_List: subImg_p2 = galImg.array[:, - subSlitPos:galImg.array.shape[1]] + subSlitPos:galImg.array.shape[1]] star_p2 = galsim.Image(subImg_p2) star_p2.setOrigin(0, 0) star_p2s.append(star_p2) @@ -423,7 +436,8 @@ class Galaxy(MockObject): isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp_p2, chip=chip, xOrderSigPlus = xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp_p2, chip=chip, pos_img_local=[xcenter_p2, ycenter_p2], # psf_model=psf_model, bandNo=i + 1, # grating_split_pos=grating_split_pos, @@ -439,7 +453,8 @@ class Galaxy(MockObject): conf=chip.sls_conf[1], isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus = xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp, chip=chip, pos_img_local=[x_nominal, y_nominal], # psf_model=psf_model, bandNo=i + 1, # grating_split_pos=grating_split_pos, @@ -453,7 +468,8 @@ class Galaxy(MockObject): conf=chip.sls_conf[0], isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus = xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp, chip=chip, pos_img_local=[x_nominal, y_nominal], # psf_model=psf_model, bandNo=i + 1, # grating_split_pos=grating_split_pos, @@ -465,7 +481,7 @@ class Galaxy(MockObject): return 1, pos_shear def getGSObj(self, psf, g1=0, g2=0, flux=None, filt=None, tel=None, exptime=150.): - if flux == None: + if flux is None: flux = self.getElectronFluxFilt(filt, tel, exptime) disk = galsim.Sersic(n=self.disk_sersic_idx, half_light_radius=self.hlr_disk, flux=1.0) diff --git a/observation_sim/mock_objects/MockObject.py b/observation_sim/mock_objects/MockObject.py index 443df4f0212a8c1f39dadf3c657e9bd73d8d2c13..6ccbe545d089bcafd38c921516e910722eb61fd2 100755 --- a/observation_sim/mock_objects/MockObject.py +++ b/observation_sim/mock_objects/MockObject.py @@ -78,6 +78,8 @@ class MockObject(object): (self.posImg.x, self.posImg.y), flush=True) self.posImg, self.fd_shear = fdmodel.get_distorted( chip=chip, pos_img=self.posImg) + if self.posImg is None: + return None, None, None, None, None if verbose: print("After field distortion:\n") print("x = %.2f, y = %.2f\n" % @@ -108,7 +110,7 @@ class MockObject(object): def drawObj_multiband(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0, exptime=150., fd_shear=None): - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) # print("nphotons_tot = ", nphotons_tot) @@ -122,7 +124,7 @@ class MockObject(object): return 2, None # Set Galsim Parameters if self.getMagFilter(filt) <= 15: - folding_threshold = 5.e-4 + folding_threshold = 5.e-8 else: folding_threshold = 5.e-3 gsp = galsim.GSParams(folding_threshold=folding_threshold) @@ -160,14 +162,17 @@ class MockObject(object): # print("nphotons_sub-band_%d = %.2f"%(i, nphotons)) # Get PSF model + EXTRA = False + if self.getMagFilter(filt) <= filt.mag_saturation-1.: + EXTRA = True psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img, bandpass=bandpass, - folding_threshold=folding_threshold) + folding_threshold=folding_threshold, extrapolate=EXTRA) # star = galsim.DeltaFunction(gsparams=gsp) # star = star.withFlux(nphotons) # star = galsim.Convolve(psf, star) star = psf.withFlux(nphotons) - stamp = star.drawImage(wcs=chip_wcs_local, offset=offset) + stamp = star.drawImage(method='no_pixel', wcs=chip_wcs_local, offset=offset) if np.sum(np.isnan(stamp.array)) > 0: continue stamp.setCenter(x_nominal, y_nominal) @@ -235,14 +240,14 @@ class MockObject(object): if chip.slsPSFOptim: for k, v in spec_orders.items(): img_s = v[0] - + nan_ids = np.isnan(img_s) if img_s[nan_ids].shape[0] > 0: img_s[nan_ids] = 0 print("DEBUG: specImg nan num is", img_s[nan_ids].shape[0]) ######################################################### # img_s, orig_off = convolveImg(img_s, psf_img_m) - orig_off = [0,0] + orig_off = [0, 0] origin_order_x = v[1] - orig_off[0] origin_order_y = v[2] - orig_off[1] @@ -257,15 +262,18 @@ class MockObject(object): continue # orders = {'A': 'order1', 'B': 'order0', 'C': 'order2', 'D': 'order-1', 'E': 'order-2'} - orders = {'A': 'order1', 'B': 'order0', 'C': 'order0', 'D': 'order0', 'E': 'order0'} + orders = {'A': 'order1', 'B': 'order0', + 'C': 'order0', 'D': 'order0', 'E': 'order0'} gratingN = chip_utils.getChipSLSGratingID(chip.chipID)[1] if pos_img_local[0] < grating_split_pos: gratingN = chip_utils.getChipSLSGratingID(chip.chipID)[0] - - chip.img_stack[gratingN][orders[k]]['w' + str(bandNo)].setOrigin(0, 0) - chip.img_stack[gratingN][orders[k]]['w' + str(bandNo)][bounds] = chip.img_stack[gratingN][orders[k]]['w' + str(bandNo)][bounds] + specImg[bounds] - chip.img_stack[gratingN][orders[k]]['w' + str(bandNo)].setOrigin(chip.bound.xmin, chip.bound.ymin) + chip.img_stack[gratingN][orders[k] + ]['w' + str(bandNo)].setOrigin(0, 0) + chip.img_stack[gratingN][orders[k]]['w' + str( + bandNo)][bounds] = chip.img_stack[gratingN][orders[k]]['w' + str(bandNo)][bounds] + specImg[bounds] + chip.img_stack[gratingN][orders[k]]['w' + + str(bandNo)].setOrigin(chip.bound.xmin, chip.bound.ymin) else: for k, v in spec_orders.items(): @@ -313,11 +321,11 @@ class MockObject(object): # specImg.wcs = local_wcs # specImg.setOrigin(origin_order_x, origin_order_y) - + # print('DEBUG: BEGIN -----------',bandNo,k) - + img_s = v[0] - + nan_ids = np.isnan(img_s) if img_s[nan_ids].shape[0] > 0: img_s[nan_ids] = 0 @@ -330,9 +338,11 @@ class MockObject(object): specImg.wcs = local_wcs specImg.setOrigin(origin_order_x, origin_order_y) try: - specImg = psf_model.get_PSF_AND_convolve_withsubImg(chip, cutImg=specImg, pos_img_local=pos_img_local, bandNo=bandNo, g_order=k, grating_split_pos=grating_split_pos) + specImg = psf_model.get_PSF_AND_convolve_withsubImg( + chip, cutImg=specImg, pos_img_local=pos_img_local, bandNo=bandNo, g_order=k, grating_split_pos=grating_split_pos) except: - psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img) + psf, pos_shear = psf_model.get_PSF( + chip=chip, pos_img=pos_img) psf_img = psf.drawImage(nx=100, ny=100, wcs=local_wcs) @@ -376,7 +386,7 @@ class MockObject(object): sedNormFactor = 1. if self.getMagFilter(filt) <= 15: - folding_threshold = 5.e-4 + folding_threshold = 5.e-8 else: folding_threshold = 5.e-3 gsp = galsim.GSParams(folding_threshold=folding_threshold) @@ -425,45 +435,51 @@ class MockObject(object): # star = galsim.DeltaFunction(gsparams=gsp) # star = star.withFlux(tel.pupil_area * exptime) - #psf list :["A","B","C","D","E"] + # psf list :["A","B","C","D","E"] starImg_List = [] try: - pos_img_local = [0,0] + pos_img_local = [0, 0] x_start = chip.x_cen/chip.pix_size - chip.npix_x / 2. y_start = chip.y_cen/chip.pix_size - chip.npix_y / 2. pos_img_local[0] = pos_img.x - x_start pos_img_local[1] = pos_img.y - y_start nnx = 0 nny = 0 - for order in ["A","B"]: + for order in ["A", "B"]: + EXTRA = False + if self.getMagFilter(filt) <= filt.mag_saturation-2.: + EXTRA = True + nnx = 2048 + nny = 2048 psf, pos_shear = psf_model.get_PSF( - chip, pos_img_local=pos_img_local, bandNo=i+1, galsimGSObject=True, g_order=order, grating_split_pos=grating_split_pos) + chip, pos_img_local=pos_img_local, bandNo=i+1, galsimGSObject=True, g_order=order, grating_split_pos=grating_split_pos, extrapolate=EXTRA, ngg=3072) # star_p = galsim.Convolve(psf, star) - star_p = psf.withFlux(tel.pupil_area * exptime) + star_p = psf.withFlux(tel.pupil_area * exptime) if nnx == 0: - starImg = star_p.drawImage(wcs=chip_wcs_local, offset=offset) + starImg = star_p.drawImage( + wcs=chip_wcs_local, offset=offset, method='no_pixel') nnx = starImg.xmax - starImg.xmin + 1 nny = starImg.ymax - starImg.ymin + 1 else: - starImg = star_p.drawImage(nx = nnx, ny = nny, wcs=chip_wcs_local, offset=offset) + starImg = star_p.drawImage( + nx=nnx, ny=nny, wcs=chip_wcs_local, offset=offset, method='no_pixel') # n1 = np.sum(np.isinf(starImg.array)) # n2 = np.sum(np.isnan(starImg.array)) # if n1>0 or n2 > 0: # print("DEBUG: MockObject, inf:%d, nan:%d"%(n1, n2)) starImg.setOrigin(0, 0) starImg_List.append(starImg) - for order in ["C","D","E"]: + for order in ["C", "D", "E"]: starImg_List.append(starImg) except: psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img) # star_p = galsim.Convolve(psf, star) - star_p = psf.withFlux(tel.pupil_area * exptime) - starImg = star_p.drawImage(wcs=chip_wcs_local, offset=offset) + star_p = psf.withFlux(tel.pupil_area * exptime) + starImg = star_p.drawImage(wcs=chip_wcs_local, offset=offset, method='no_pixel') starImg.setOrigin(0, 0) - for order in ["A","B","C","D","E"]: + for order in ["A", "B", "C", "D", "E"]: starImg_List.append(starImg) - # psf_tmp = galsim.Gaussian(sigma=0.002) # star = galsim.Convolve(psf_tmp, star) @@ -472,14 +488,14 @@ class MockObject(object): origin_star = [y_nominal - (starImg.center.y - starImg.ymin), x_nominal - (starImg.center.x - starImg.xmin)] - + gal_origin = [origin_star[0], origin_star[1]] gal_end = [origin_star[0] + starImg.array.shape[0] - 1, origin_star[1] + starImg.array.shape[1] - 1] if gal_origin[1] < grating_split_pos_chip < gal_end[1]: subSlitPos = int(grating_split_pos_chip - gal_origin[1]) # part img disperse - star_p1s=[] + star_p1s = [] for starImg in starImg_List: subImg_p1 = starImg.array[:, 0:subSlitPos] @@ -498,17 +514,17 @@ class MockObject(object): isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp_p1, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp_p1, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp_p1, chip=chip, pos_img_local=[xcenter_p1, ycenter_p1], # psf_model=psf_model, bandNo=i+1, grating_split_pos=grating_split_pos, # local_wcs=chip_wcs_local, pos_img=pos_img) - - star_p2s=[] + star_p2s = [] for starImg in starImg_List: subImg_p2 = starImg.array[:, - subSlitPos:starImg.array.shape[1]] + subSlitPos:starImg.array.shape[1]] star_p2 = galsim.Image(subImg_p2) star_p2.setOrigin(0, 0) star_p2s.append(star_p2) @@ -524,7 +540,8 @@ class MockObject(object): isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp_p2, chip=chip, pos_img_local=[xcenter_p2, ycenter_p2], # psf_model=psf_model, bandNo=i + 1, grating_split_pos=grating_split_pos, # local_wcs=chip_wcs_local, pos_img=pos_img) @@ -539,7 +556,8 @@ class MockObject(object): conf=chip.sls_conf[1], isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp, chip=chip, pos_img_local=[x_nominal, y_nominal], # psf_model=psf_model, bandNo=i + 1, grating_split_pos=grating_split_pos, # local_wcs=chip_wcs_local, pos_img=pos_img) @@ -552,7 +570,8 @@ class MockObject(object): conf=chip.sls_conf[0], isAlongY=0, flat_cube=flat_cube) - self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) + self.addSLStoChipImage( + sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local) # pos_shear = self.addSLStoChipImageWithPSF(sdp=sdp, chip=chip, pos_img_local=[x_nominal, y_nominal], # psf_model=psf_model, bandNo=i + 1, grating_split_pos=grating_split_pos, # local_wcs=chip_wcs_local, pos_img=pos_img) @@ -572,7 +591,7 @@ class MockObject(object): def drawObj_PSF(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0, exptime=150., fd_shear=None, chip_output=None): - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) # print("nphotons_tot = ", nphotons_tot) @@ -641,7 +660,7 @@ class MockObject(object): os.makedirs(fn, exist_ok=True) fn = fn + "/ccd_{:}".format(chip.chipID) + \ "_psf_"+str(self.param['id'])+".fits" - if fn != None: + if fn is not None: if os.path.exists(fn): os.remove(fn) hdu = fitsio.PrimaryHDU() diff --git a/observation_sim/mock_objects/Quasar.py b/observation_sim/mock_objects/Quasar.py index b27cb8ce97a6dff472cc08ab9752dc22f05424e4..3589b0d1e80f3977b9fc83f7624b02557a0b24ae 100755 --- a/observation_sim/mock_objects/Quasar.py +++ b/observation_sim/mock_objects/Quasar.py @@ -98,7 +98,7 @@ class Quasar(MockObject): del self.sed def getGSObj(self, psf, g1=0, g2=0, flux=None, filt=None, tel=None, exptime=150.): - if flux == None: + if flux is None: flux = self.getElectronFluxFilt(filt, tel, exptime) qso = galsim.Gaussian(sigma=1.e-8, flux=1.) qso = qso.withFlux(flux) diff --git a/observation_sim/mock_objects/SpecDisperser/SpecDisperser.py b/observation_sim/mock_objects/SpecDisperser/SpecDisperser.py index 2b2d7c29baa9e9965725da0cf92fac98b8495ae2..fb65af3275ca30f9b91fc8f705f360482bd68f4d 100644 --- a/observation_sim/mock_objects/SpecDisperser/SpecDisperser.py +++ b/observation_sim/mock_objects/SpecDisperser/SpecDisperser.py @@ -50,8 +50,21 @@ def rotate90(array_orig=None, xc=0, yc=0, isClockwise=0): class SpecDisperser(object): - def __init__(self, orig_img=None, xcenter=0, ycenter=0, origin=[100, 100], tar_spec=None, band_start=6200, - band_end=10000, isAlongY=0, conf='../param/CONF/csst.conf', gid=0, flat_cube=None, ignoreBeam=[]): + def __init__( + self, + orig_img=None, + xcenter=0, + ycenter=0, + origin=[100, 100], + tar_spec=None, + band_start=6200, + band_end=10000, + isAlongY=0, + conf="../param/CONF/csst.conf", + gid=0, + flat_cube=None, + ignoreBeam=[], + ): """ orig_img: normal image,galsim image xcenter, ycenter: the center of galaxy in orig_img @@ -66,23 +79,21 @@ class SpecDisperser(object): # self.img_y = orig_img.shape[0] # 5 orders, A, B , - orderName=["A","B","C","D","E"] + orderName = ["A", "B", "C", "D", "E"] self.orig_img_orders = OrderedDict() if isinstance(orig_img, list): orig_img_list = orig_img list_len = len(orig_img_list) if list_len < 5: - for i in np.arange(5-list_len): - orig_img_list.append(orig_img_list[list_len-1]) + for i in np.arange(5 - list_len): + orig_img_list.append(orig_img_list[list_len - 1]) for i, k in enumerate(orig_img_list): self.orig_img_orders[orderName[i]] = k - if isinstance(orig_img, galsim.Image): for i in np.arange(5): self.orig_img_orders[orderName[i]] = orig_img - orig_img_one = self.orig_img_orders["A"] self.thumb_img = np.abs(orig_img_one.array) @@ -99,8 +110,12 @@ class SpecDisperser(object): self.flat_cube = flat_cube if self.isAlongY == 1: for order in orderName: - self.orig_img_orders[order], self.thumb_x, self.thumb_y = rotate90(array_orig=self.orig_img_orders[order], xc=orig_img_one.center.x, - yc=orig_img_one.center.y, isClockwise=1) + self.orig_img_orders[order], self.thumb_x, self.thumb_y = rotate90( + array_orig=self.orig_img_orders[order], + xc=orig_img_one.center.x, + yc=orig_img_one.center.y, + isClockwise=1, + ) # self.thumb_img, self.thumb_x, self.thumb_y = rotate90(array_orig=self.thumb_img, xc=orig_img_one.center.x, # yc=orig_img_one.center.y, isClockwise=1) @@ -139,6 +154,7 @@ class SpecDisperser(object): from .disperse_c import interp from .disperse_c import disperse + # from MockObject.disperse_c import disperse self.thumb_img = np.abs(self.orig_img_orders[beam].array) self.thumb_x = self.orig_img_orders[beam].center.x @@ -146,11 +162,12 @@ class SpecDisperser(object): self.img_sh = self.orig_img_orders[beam].array.shape dx = self.grating_conf.dxlam[beam] xoff = 0 - ytrace_beam, lam_beam = self.grating_conf.get_beam_trace(x=self.xcenter, y=self.ycenter, dx=(dx + xoff), - beam=beam) + ytrace_beam, lam_beam = self.grating_conf.get_beam_trace( + x=self.xcenter, y=self.ycenter, dx=(dx + xoff), beam=beam + ) # Account for pixel centering of the trace - yfrac_beam = ytrace_beam - floor(ytrace_beam+0.5) + yfrac_beam = ytrace_beam - floor(ytrace_beam + 0.5) ysens = lam_beam * 0 lam_index = argsort(lam_beam) @@ -160,8 +177,8 @@ class SpecDisperser(object): (self.band_end - self.band_start) / 0.1)) thri = interpolate.interp1d( - conf_sens['WAVELENGTH'], conf_sens['SENSITIVITY']) - spci = interpolate.interp1d(self.spec['WAVELENGTH'], self.spec['FLUX']) + conf_sens["WAVELENGTH"], conf_sens["SENSITIVITY"]) + spci = interpolate.interp1d(self.spec["WAVELENGTH"], self.spec["FLUX"]) beam_thr = thri(lam_intep) spec_sample = spci(lam_intep) @@ -179,8 +196,9 @@ class SpecDisperser(object): # # self.writerSensitivityFile(conffile = self.grating_conf_file, beam = beam, w = lam_beam[lam_index], sens = ysensitivity[lam_index]) - ysens[lam_index] = interp.interp_conserve_c(lam_beam[lam_index], lam_intep, bean_thr_spec, integrate=1, left=0, - right=0) + ysens[lam_index] = interp.interp_conserve_c( + lam_beam[lam_index], lam_intep, bean_thr_spec, integrate=1, left=0, right=0 + ) sensitivity_beam = ysens @@ -196,7 +214,7 @@ class SpecDisperser(object): dxpix = dx - dx[0] + x0[1] - dyc = cast[int](np.floor(ytrace_beam+0.5)) + dyc = cast[int](np.floor(ytrace_beam + 0.5)) # dypix = cast[int](np.floor(ytrace_beam - dyc[0] + x0[0] + 0.5)) dypix = dyc - dyc[0] + x0[0] @@ -204,7 +222,7 @@ class SpecDisperser(object): frac_ids = yfrac_beam < 0 dypix[frac_ids] = dypix[frac_ids] - 1 - yfrac_beam[frac_ids] = 1+yfrac_beam[frac_ids] + yfrac_beam[frac_ids] = 1 + yfrac_beam[frac_ids] flat_index = idx[dypix, dxpix] @@ -233,7 +251,7 @@ class SpecDisperser(object): sub_flat_cube = zeros( [len(self.flat_cube), beam_sh[0], beam_sh[1]]) - sub_flat_cube[0] = sub_flat_cube[0] + 1. + sub_flat_cube[0] = sub_flat_cube[0] + 1.0 overlap_flag = 1 @@ -260,33 +278,38 @@ class SpecDisperser(object): overlap_flag = 0 if overlap_flag == 1: - sub_flat_cube[:, beam_y_s-originOut_y:beam_y_e-originOut_y+1, beam_x_s-originOut_x:beam_x_e - - originOut_x+1] = self.flat_cube[:, beam_y_s:beam_y_e+1, beam_x_s:beam_x_e+1] + sub_flat_cube[ + :, + beam_y_s - originOut_y: beam_y_e - originOut_y + 1, + beam_x_s - originOut_x: beam_x_e - originOut_x + 1, + ] = self.flat_cube[:, beam_y_s: beam_y_e + 1, beam_x_s: beam_x_e + 1] for i in arange(0, len(self.flat_cube), 1): beam_flat[:, i] = sub_flat_cube[i].flatten() -# beam_flat = zeros([len(modelf), len(self.flat_cube)]) -# flat_sh = self.flat_cube[0].shape -# for i in arange(0, beam_sh[0], 1): -# for j in arange(0, beam_sh[1], 1): -# k = i * beam_sh[1] + j -# if originOut_y + i >= flat_sh[0] or originOut_y + i < 0 or originOut_x + j>= flat_sh[1] or originOut_x+j < 0: -# temp_bf = np.zeros_like(self.flat_cube[:, 0, 0]) -# temp_bf[0] = 1.0 -# beam_flat[k] = temp_bf -# else: -# beam_flat[k] = self.flat_cube[:, originOut_y + i, originOut_x + j] - - status = disperse.disperse_grism_object(self.thumb_img.astype(np.float32), - flat_index[nonz], - yfrac_beam[nonz], - sensitivity_beam[nonz], - modelf, x0, - array(self.img_sh, - dtype=int64), - array(beam_sh, dtype=int64), - beam_flat, - lam_beam[lam_index][nonz]) + # beam_flat = zeros([len(modelf), len(self.flat_cube)]) + # flat_sh = self.flat_cube[0].shape + # for i in arange(0, beam_sh[0], 1): + # for j in arange(0, beam_sh[1], 1): + # k = i * beam_sh[1] + j + # if originOut_y + i >= flat_sh[0] or originOut_y + i < 0 or originOut_x + j>= flat_sh[1] or originOut_x+j < 0: + # temp_bf = np.zeros_like(self.flat_cube[:, 0, 0]) + # temp_bf[0] = 1.0 + # beam_flat[k] = temp_bf + # else: + # beam_flat[k] = self.flat_cube[:, originOut_y + i, originOut_x + j] + + status = disperse.disperse_grism_object( + self.thumb_img.astype(np.float32), + flat_index[nonz], + yfrac_beam[nonz], + sensitivity_beam[nonz], + modelf, + x0, + array(self.img_sh, dtype=int64), + array(beam_sh, dtype=int64), + beam_flat, + lam_beam[lam_index][nonz], + ) model = modelf.reshape(beam_sh) # n1 = np.sum(np.isinf(model)) @@ -296,27 +319,27 @@ class SpecDisperser(object): # if n1>0 or n2 > 0: # print("DEBUG: SpecDisperser, inf:%d, nan:%d--------%d,%d"%(n1, n2, n3, n4)) # print(dypix) - # n1 = np.sum(np.isinf(self.thumb_img.astype(np.float32))) - # n2 = np.sum(np.isnan(self.thumb_img.astype(np.float32))) - # n3 = np.sum(np.isinf(yfrac_beam)) - # n4 = np.sum(np.isnan(yfrac_beam)) - # n5 = np.sum(np.isinf(sensitivity_beam)) - # n6 = np.sum(np.isnan(sensitivity_beam)) - # print("DEBUG: SpecDisperser, innput ---inf:%d, nan:%d, yfrac_beam:%d/%d, sensitivity_beam:%d/%d"%(n1, n2, n3, n4, n5, n6)) + # n1 = np.sum(np.isinf(self.thumb_img.astype(np.float32))) + # n2 = np.sum(np.isnan(self.thumb_img.astype(np.float32))) + # n3 = np.sum(np.isinf(yfrac_beam)) + # n4 = np.sum(np.isnan(yfrac_beam)) + # n5 = np.sum(np.isinf(sensitivity_beam)) + # n6 = np.sum(np.isnan(sensitivity_beam)) + # print("DEBUG: SpecDisperser, innput ---inf:%d, nan:%d, yfrac_beam:%d/%d, sensitivity_beam:%d/%d"%(n1, n2, n3, n4, n5, n6)) self.beam_flux[beam] = sum(modelf) if self.isAlongY == 1: model, _, _ = rotate90(array_orig=model, isClockwise=0) return model, originOut_x, originOut_y, dxpix, dypix, lam_beam, ysens - def writerSensitivityFile(self, conffile='', beam='', w=None, sens=None): - orders = {'A': '1st', 'B': '0st', 'C': '2st', 'D': '-1st', 'E': '-2st'} + def writerSensitivityFile(self, conffile="", beam="", w=None, sens=None): + orders = {"A": "1st", "B": "0st", "C": "2st", "D": "-1st", "E": "-2st"} sens_file_name = conffile[0:-5] + \ - '_sensitivity_' + orders[beam] + '.fits' - if not os.path.exists(sens_file_name) == True: + "_sensitivity_" + orders[beam] + ".fits" + if os.path.exists(sens_file_name) is False: senstivity_out = Table( - array([w, sens]).T, names=('WAVELENGTH', 'SENSITIVITY')) - senstivity_out.write(sens_file_name, format='fits') + array([w, sens]).T, names=("WAVELENGTH", "SENSITIVITY")) + senstivity_out.write(sens_file_name, format="fits") """ @@ -324,8 +347,8 @@ Demonstrate aXe trace polynomials. """ -class aXeConf(): - def __init__(self, conf_file='WFC3.IR.G141.V2.5.conf'): +class aXeConf: + def __init__(self, conf_file="WFC3.IR.G141.V2.5.conf"): """Read an aXe-compatible configuration file Parameters @@ -340,17 +363,17 @@ class aXeConf(): self.count_beam_orders() # Global XOFF/YOFF offsets - if 'XOFF' in self.conf.keys(): - self.xoff = np.float(self.conf['XOFF']) + if "XOFF" in self.conf.keys(): + self.xoff = np.float(self.conf["XOFF"]) else: - self.xoff = 0. + self.xoff = 0.0 - if 'YOFF' in self.conf.keys(): - self.yoff = np.float(self.conf['YOFF']) + if "YOFF" in self.conf.keys(): + self.yoff = np.float(self.conf["YOFF"]) else: - self.yoff = 0. + self.yoff = 0.0 - def read_conf_file(self, conf_file='WFC3.IR.G141.V2.5.conf'): + def read_conf_file(self, conf_file="WFC3.IR.G141.V2.5.conf"): """Read an aXe config file, convert floats and arrays Parameters @@ -366,11 +389,11 @@ class aXeConf(): lines = open(conf_file).readlines() for line in lines: # empty / commented lines - if (line.startswith('#')) | (line.strip() == '') | ('"' in line): + if (line.startswith("#")) | (line.strip() == "") | ('"' in line): continue # split the line, taking out ; and # comments - spl = line.split(';')[0].split('#')[0].split() + spl = line.split(";")[0].split("#")[0].split() param = spl[0] if len(spl) > 2: value = np.cast[float](spl[1:]) @@ -385,22 +408,20 @@ class aXeConf(): return conf def count_beam_orders(self): - """Get the maximum polynomial order in DYDX or DLDP for each beam - """ + """Get the maximum polynomial order in DYDX or DLDP for each beam""" self.orders = {} - for beam in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']: + for beam in ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]: order = 0 - while 'DYDX_{0:s}_{1:d}'.format(beam, order) in self.conf.keys(): + while "DYDX_{0:s}_{1:d}".format(beam, order) in self.conf.keys(): order += 1 - while 'DLDP_{0:s}_{1:d}'.format(beam, order) in self.conf.keys(): + while "DLDP_{0:s}_{1:d}".format(beam, order) in self.conf.keys(): order += 1 self.orders[beam] = order - 1 def get_beams(self): - """Get beam parameters and read sensitivity curves - """ + """Get beam parameters and read sensitivity curves""" import os from collections import OrderedDict from astropy.table import Table, Column @@ -413,12 +434,15 @@ class aXeConf(): for beam in self.orders: if self.orders[beam] > 0: self.beams.append(beam) - self.dxlam[beam] = np.arange(self.conf['BEAM{0}'.format(beam)].min(), - self.conf['BEAM{0}'.format(beam)].max(), dtype=int) + self.dxlam[beam] = np.arange( + self.conf["BEAM{0}".format(beam)].min(), self.conf["BEAM{0}".format(beam)].max(), dtype=int + ) self.nx[beam] = int(self.dxlam[beam].max() - self.dxlam[beam].min()) + 1 self.sens[beam] = Table.read( - '{0}/{1}'.format(os.path.dirname(self.conf_file), self.conf['SENSITIVITY_{0}'.format(beam)])) + "{0}/{1}".format(os.path.dirname(self.conf_file), + self.conf["SENSITIVITY_{0}".format(beam)]) + ) # self.sens[beam].wave = np.cast[np.double](self.sens[beam]['WAVELENGTH']) # self.sens[beam].sens = np.cast[np.double](self.sens[beam]['SENSITIVITY']) @@ -471,41 +495,41 @@ class aXeConf(): return a def evaluate_dp(self, dx, dydx): - """Evalate arc length along the trace given trace polynomial coefficients + # """Evalate arc length along the trace given trace polynomial coefficients - Parameters - ---------- - dx : array-like - x pixel to evaluate + # Parameters + # ---------- + # dx : array-like + # x pixel to evaluate - dydx : array-like - Coefficients of the trace polynomial + # dydx : array-like + # Coefficients of the trace polynomial - Returns - ------- - dp : array-like - Arc length along the trace at position `dx`. + # Returns + # ------- + # dp : array-like + # Arc length along the trace at position `dx`. - For `dydx` polynomial orders 0, 1 or 2, integrate analytically. - Higher orders must be integrated numerically. + # For `dydx` polynomial orders 0, 1 or 2, integrate analytically. + # Higher orders must be integrated numerically. - **Constant:** - .. math:: dp = dx + # **Constant:** + # .. math:: dp = dx - **Linear:** - .. math:: dp = \sqrt{1+\mathrm{DYDX}[1]}\cdot dx + # **Linear:** + # .. math:: dp = \sqrt{1+\mathrm{DYDX}[1]}\cdot dx - **Quadratic:** - .. math:: u = \mathrm{DYDX}[1] + 2\ \mathrm{DYDX}[2]\cdot dx + # **Quadratic:** + # .. math:: u = \mathrm{DYDX}[1] + 2\ \mathrm{DYDX}[2]\cdot dx - .. math:: dp = (u \sqrt{1+u^2} + \mathrm{arcsinh}\ u) / (4\cdot \mathrm{DYDX}[2]) + # .. math:: dp = (u \sqrt{1+u^2} + \mathrm{arcsinh}\ u) / (4\cdot \mathrm{DYDX}[2]) - """ + # """ # dp is the arc length along the trace # $\lambda = dldp_0 + dldp_1 dp + dldp_2 dp^2$ ... poly_order = len(dydx) - 1 - if (poly_order == 2): + if poly_order == 2: if np.abs(np.unique(dydx[2])).max() == 0: poly_order = 1 @@ -515,10 +539,9 @@ class aXeConf(): dp = np.sqrt(1 + dydx[1] ** 2) * (dx) elif poly_order == 2: # quadratic trace u0 = dydx[1] + 2 * dydx[2] * (0) - dp0 = (u0 * np.sqrt(1 + u0 ** 2) + np.arcsinh(u0)) / (4 * dydx[2]) + dp0 = (u0 * np.sqrt(1 + u0**2) + np.arcsinh(u0)) / (4 * dydx[2]) u = dydx[1] + 2 * dydx[2] * (dx) - dp = (u * np.sqrt(1 + u ** 2) + np.arcsinh(u)) / \ - (4 * dydx[2]) - dp0 + dp = (u * np.sqrt(1 + u**2) + np.arcsinh(u)) / (4 * dydx[2]) - dp0 else: # high order shape, numerical integration along trace # (this can be slow) @@ -530,7 +553,7 @@ class aXeConf(): dyfull += i * dydx[i] * (xfull - 0.5) ** (i - 1) # Integrate from 0 to dx / -dx - dpfull = xfull * 0. + dpfull = xfull * 0.0 lt0 = xfull < 0 if lt0.sum() > 1: dpfull[lt0] = np.cumsum( @@ -548,7 +571,7 @@ class aXeConf(): return dp - def get_beam_trace(self, x=507, y=507, dx=0., beam='A'): + def get_beam_trace(self, x=507, y=507, dx=0.0, beam="A"): """Get an aXe beam trace for an input reference pixel and list of output x pixels `dx` Parameters @@ -579,17 +602,17 @@ class aXeConf(): xi, yi = x - self.xoff, y - self.yoff xoff_beam = self.field_dependent( - xi, yi, self.conf['XOFF_{0}'.format(beam)]) + xi, yi, self.conf["XOFF_{0}".format(beam)]) yoff_beam = self.field_dependent( - xi, yi, self.conf['YOFF_{0}'.format(beam)]) + xi, yi, self.conf["YOFF_{0}".format(beam)]) # y offset of trace (DYDX) dydx = np.zeros(NORDER) # 0 #+1.e-80 dydx = [0] * NORDER for i in range(NORDER): - if 'DYDX_{0:s}_{1:d}'.format(beam, i) in self.conf.keys(): - coeffs = self.conf['DYDX_{0:s}_{1:d}'.format(beam, i)] + if "DYDX_{0:s}_{1:d}".format(beam, i) in self.conf.keys(): + coeffs = self.conf["DYDX_{0:s}_{1:d}".format(beam, i)] dydx[i] = self.field_dependent(xi, yi, coeffs) # $dy = dydx_0+dydx_1 dx+dydx_2 dx^2+$ ... @@ -603,14 +626,20 @@ class aXeConf(): dldp = [0] * NORDER for i in range(NORDER): - if 'DLDP_{0:s}_{1:d}'.format(beam, i) in self.conf.keys(): - coeffs = self.conf['DLDP_{0:s}_{1:d}'.format(beam, i)] + if "DLDP_{0:s}_{1:d}".format(beam, i) in self.conf.keys(): + coeffs = self.conf["DLDP_{0:s}_{1:d}".format(beam, i)] dldp[i] = self.field_dependent(xi, yi, coeffs) - self.eval_input = {'x': x, 'y': y, 'beam': beam, 'dx': dx} - self.eval_output = {'xi': xi, 'yi': yi, 'dldp': dldp, 'dydx': dydx, - 'xoff_beam': xoff_beam, 'yoff_beam': yoff_beam, - 'dy': dy} + self.eval_input = {"x": x, "y": y, "beam": beam, "dx": dx} + self.eval_output = { + "xi": xi, + "yi": yi, + "dldp": dldp, + "dydx": dydx, + "xoff_beam": xoff_beam, + "yoff_beam": yoff_beam, + "dy": dy, + } dp = self.evaluate_dp(dx - xoff_beam, dydx) # ## dp is the arc length along the trace @@ -648,13 +677,13 @@ class aXeConf(): # dp = np.interp(dx-xoff_beam, xfull, dpfull) # Evaluate dldp - lam = dp * 0. + lam = dp * 0.0 for i in range(NORDER): - lam += dldp[i] * dp ** i + lam += dldp[i] * dp**i return dy, lam - def show_beams(self, beams=['E', 'D', 'C', 'B', 'A']): + def show_beams(self, beams=["E", "D", "C", "B", "A"]): """ Make a demo plot of the beams of a given configuration file """ @@ -663,45 +692,47 @@ class aXeConf(): x0, x1 = 507, 507 dx = np.arange(-800, 1200) - if 'WFC3.UV' in self.conf_file: + if "WFC3.UV" in self.conf_file: x0, x1 = 2073, 250 dx = np.arange(-1200, 1200) - if 'G800L' in self.conf_file: + if "G800L" in self.conf_file: x0, x1 = 2124, 1024 dx = np.arange(-1200, 1200) s = 200 # marker size fig = plt.figure(figsize=[10, 3]) - plt.scatter(0, 0, marker='s', s=s, color='black', edgecolor='0.8', - label='Direct') + plt.scatter(0, 0, marker="s", s=s, color="black", + edgecolor="0.8", label="Direct") for beam in beams: - if 'XOFF_{0}'.format(beam) not in self.conf.keys(): + if "XOFF_{0}".format(beam) not in self.conf.keys(): continue xoff = self.field_dependent( - x0, x1, self.conf['XOFF_{0}'.format(beam)]) + x0, x1, self.conf["XOFF_{0}".format(beam)]) dy, lam = self.get_beam_trace(x0, x1, dx=dx, beam=beam) - xlim = self.conf['BEAM{0}'.format(beam)] + xlim = self.conf["BEAM{0}".format(beam)] ok = (dx >= xlim[0]) & (dx <= xlim[1]) - plt.scatter(dx[ok] + xoff, dy[ok], c=lam[ok] / 1.e4, marker='s', s=s, - alpha=0.5, edgecolor='None') - plt.text(np.median(dx[ok]), np.median(dy[ok]) + 1, beam, - ha='center', va='center', fontsize=14) - print('Beam {0}, lambda=({1:.1f} - {2:.1f})'.format(beam, + plt.scatter(dx[ok] + xoff, dy[ok], c=lam[ok] / 1.0e4, + marker="s", s=s, alpha=0.5, edgecolor="None") + plt.text(np.median(dx[ok]), np.median( + dy[ok]) + 1, beam, ha="center", va="center", fontsize=14) + print("Beam {0}, lambda=({1:.1f} - {2:.1f})".format(beam, lam[ok].min(), lam[ok].max())) plt.grid() - plt.xlabel(r'$\Delta x$') - plt.ylabel(r'$\Delta y$') + plt.xlabel(r"$\Delta x$") + plt.ylabel(r"$\Delta y$") cb = plt.colorbar(pad=0.01, fraction=0.05) - cb.set_label(r'$\lambda\,(\mu\mathrm{m})$') + cb.set_label(r"$\lambda\,(\mu\mathrm{m})$") plt.title(self.conf_file) plt.tight_layout() - plt.savefig('{0}.pdf'.format(self.conf_file)) + plt.savefig("{0}.pdf".format(self.conf_file)) # def load_grism_config(conf_file): + + # """Load parameters from an aXe configuration file # Parameters diff --git a/observation_sim/mock_objects/SpecDisperser/__init__.py b/observation_sim/mock_objects/SpecDisperser/__init__.py index c5d9a483ce28eff56ce4c496e41d13f4d2290b8e..6165f8d77ff82e403d112b2553496a2657631a17 100644 --- a/observation_sim/mock_objects/SpecDisperser/__init__.py +++ b/observation_sim/mock_objects/SpecDisperser/__init__.py @@ -1,2 +1,2 @@ from .SpecDisperser import * -from .disperse_c import disperse, interp \ No newline at end of file +from .disperse_c import disperse, interp diff --git a/observation_sim/mock_objects/SpecDisperser/setup_c.py b/observation_sim/mock_objects/SpecDisperser/setup_c.py index feceb9715239ddeb7c809cc172b8c9c66f6f18d3..e5a4467b4145ef8b5b6a5edf2c5d116c1a0c75ee 100644 --- a/observation_sim/mock_objects/SpecDisperser/setup_c.py +++ b/observation_sim/mock_objects/SpecDisperser/setup_c.py @@ -8,16 +8,16 @@ import numpy extensions = [ Extension("disperse_c.interp", ["disperse_c/interp.pyx"], - include_dirs = [numpy.get_include()], - libraries=["m"]), - + include_dirs=[numpy.get_include()], + libraries=["m"]), + Extension("disperse_c.disperse", ["disperse_c/disperse.pyx"], - include_dirs = [numpy.get_include()], - libraries=["m"]), + include_dirs=[numpy.get_include()], + libraries=["m"]), ] setup( - name = "slssim_disperse", - ext_modules = cythonize(extensions), + name="slssim_disperse", + ext_modules=cythonize(extensions), ) diff --git a/observation_sim/mock_objects/Stamp.py b/observation_sim/mock_objects/Stamp.py index 51de71c9b42cbcbc54f5a02045c4901030597aa3..c16c26f397d57f2548ef80c7edca32125eba5997 100644 --- a/observation_sim/mock_objects/Stamp.py +++ b/observation_sim/mock_objects/Stamp.py @@ -21,7 +21,7 @@ class Stamp(MockObject): del self.sed def drawObj_multiband(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0, exptime=150., fd_shear=None): - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) try: @@ -92,7 +92,7 @@ class Stamp(MockObject): else: gal = gal + gal_temp - stamp = gal.drawImage(wcs=chip_wcs_local, offset=offset) + stamp = gal.drawImage(method='no_pixel', wcs=chip_wcs_local, offset=offset) if np.sum(np.isnan(stamp.array)) > 0: # ERROR happens return 2, pos_shear diff --git a/observation_sim/mock_objects/Star.py b/observation_sim/mock_objects/Star.py index c27b4bab7036874913088a156f07b137dd340f13..c0aceb3d1ea5357f09a41fee421ca95c561e4311 100755 --- a/observation_sim/mock_objects/Star.py +++ b/observation_sim/mock_objects/Star.py @@ -22,7 +22,7 @@ class Star(MockObject): del self.sed def getGSObj(self, psf, g1=0, g2=0, flux=None, filt=None, tel=None, exptime=150.): - if flux == None: + if flux is None: flux = self.getElectronFluxFilt(filt, tel, exptime) # star = galsim.Gaussian(sigma=1.e-8, flux=1.) star = galsim.DeltaFunction() @@ -35,7 +35,7 @@ class Star(MockObject): raise ValueError( "!!!The number of PSF profiles and the number of bandpasses must be equal.") objs = [] - if nphotons_tot == None: + if nphotons_tot is None: nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime) try: diff --git a/observation_sim/mock_objects/_util.py b/observation_sim/mock_objects/_util.py index f5b0c06db23e4f4a1bd85e0dae3f3b413bbf79c4..5707fd4f561cb7ddaa7c8b86d86a397cfa7d6237 100755 --- a/observation_sim/mock_objects/_util.py +++ b/observation_sim/mock_objects/_util.py @@ -58,7 +58,7 @@ def seds(sedlistn, seddir="./", unit="A"): read SEDs and save into Python dictionary Parameters: - sedlistn: filename of the sed template list and corresponding intrinsic + sedlistn: filename of the sed template list and corresponding intrinsic extinction model, see tmag_dz.py for detailes listdir: directory of the list unit: wavelength unit of the input templates @@ -420,7 +420,7 @@ def sed2mag(mag_i, sedCat, filter_list, redshift=0.0, av=0.0, redden=0): def eObs(e1, e2, g1, g2): """ - Calculate the sheared (observed) ellipticity using the + Calculate the sheared (observed) ellipticity using the intrinsic ellipticity and cosmic shear components. Parameters: @@ -558,7 +558,7 @@ def getNormFactorForSpecWithABMAG(ABMag=20., spectrum=None, norm_thr=None, sWave eWave: the end of norm_thr Return: - the normalization factor flux of AB system(fix a band and magnitude ) /the flux of inpute spectrum(fix a band) + the normalization factor flux of AB system(fix a band and magnitude ) /the flux of inpute spectrum(fix a band) """ spectrumi = interpolate.interp1d(spectrum['WAVELENGTH'], spectrum['FLUX']) norm_thri = interpolate.interp1d( diff --git a/observation_sim/psf/FieldDistortion.py b/observation_sim/psf/FieldDistortion.py index 506bbba487cc91b1c7a97418eb648c2a53491d9a..ff5faeab84a8237afca3471e400e13c9d8e2446c 100644 --- a/observation_sim/psf/FieldDistortion.py +++ b/observation_sim/psf/FieldDistortion.py @@ -62,7 +62,8 @@ class FieldDistortion(object): the distored position. """ if not self.isContainObj_FD(chip=chip, pos_img=pos_img): - return galsim.PositionD(-1, -1), None + # return galsim.PositionD(-1, -1), None + return None, None if not img_rot: img_rot = np.radians(self.img_rot) else: diff --git a/observation_sim/psf/PSFGauss.py b/observation_sim/psf/PSFGauss.py index e3036d9293c37397505a4abf65b367b5d9429a8f..db8b3eac48d53645f75dade4734181115be40279 100644 --- a/observation_sim/psf/PSFGauss.py +++ b/observation_sim/psf/PSFGauss.py @@ -50,7 +50,7 @@ class PSFGauss(PSFModel): Return: the flux ratio """ - if pscale == None: + if pscale is None: pscale = self.pix_size gaussx = galsim.Gaussian(flux=1.0, sigma=sig) gaussImg = gaussx.drawImage(scale=pscale, method='no_pixel') @@ -68,7 +68,7 @@ class PSFGauss(PSFModel): return the fwhm in arcsec """ - if pscale == None: + if pscale is None: pscale = self.pix_size err = 1.0e-3 nxx = 100 @@ -122,4 +122,4 @@ class PSFGauss(PSFModel): # return ell, beta, qr PSFshear = galsim.Shear(e=ell, beta=beta*galsim.radians) - return self.psf.shear(PSFshear), PSFshear \ No newline at end of file + return self.psf.shear(PSFshear), PSFshear diff --git a/observation_sim/psf/PSFInterp.py b/observation_sim/psf/PSFInterp.py index 97435ad048225969fd5287e1dd01a26ef2eb639f..d21e75fcc81d030f41022da26a40e8e460ddc809 100644 --- a/observation_sim/psf/PSFInterp.py +++ b/observation_sim/psf/PSFInterp.py @@ -13,13 +13,14 @@ import galsim import h5py from observation_sim.psf.PSFModel import PSFModel +from observation_sim.psf._util import psf_extrapolate NPSF = 900 # ***# 30*30 PixSizeInMicrons = 5. # ***# in microns -### find neighbors-KDtree### +# find neighbors-KDtree # def findNeighbors(tx, ty, px, py, dr=0.1, dn=1, OnlyDistance=True): """ find nearest neighbors by 2D-KDTree @@ -40,9 +41,9 @@ def findNeighbors(tx, ty, px, py, dr=0.1, dn=1, OnlyDistance=True): dataq = [] rr = dr - if OnlyDistance == True: + if OnlyDistance is True: dataq = tree.query_ball_point([tx, ty], rr) - if OnlyDistance == False: + if OnlyDistance is False: while len(dataq) < dn: dataq = tree.query_ball_point([tx, ty], rr) rr += dr @@ -51,7 +52,7 @@ def findNeighbors(tx, ty, px, py, dr=0.1, dn=1, OnlyDistance=True): dataq = np.array(dataq)[ddSortindx[0:dn]] return dataq -### find neighbors-hoclist### +# find neighbors-hoclist# def hocBuild(partx, party, nhocx, nhocy, dhocx, dhocy): @@ -139,7 +140,7 @@ def findNeighbors_hoclist(px, py, tx=None, ty=None, dn=4, hoc=None, hoclist=None return neigh -### PSF-IDW### +# PSF-IDW# def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=True, hoc=None, hoclist=None, PSFCentroidWgt=False): """ psf interpolation by IDW @@ -163,7 +164,7 @@ def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=Tru npsf = PSFMat[:, :, :].shape[0] psfWeight = np.zeros([npsf]) - if OnlyNeighbors == True: + if OnlyNeighbors is True: if hoc is None: neigh = findNeighbors(px, py, cen_col, cen_row, dr=5., dn=4, OnlyDistance=False) @@ -175,7 +176,7 @@ def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=Tru neighFlag[neigh] = 1 for ipsf in range(npsf): - if OnlyNeighbors == True: + if OnlyNeighbors is True: if neighFlag[ipsf] != 1: continue @@ -195,7 +196,7 @@ def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=Tru psfMaker = np.zeros([ngy, ngx], dtype=np.float32) for ipsf in range(npsf): - if OnlyNeighbors == True: + if OnlyNeighbors is True: if neighFlag[ipsf] != 1: continue @@ -208,7 +209,7 @@ def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=Tru return psfMaker -### define PSFInterp### +# define PSFInterp# class PSFInterp(PSFModel): def __init__(self, chip, npsf=NPSF, PSF_data=None, PSF_data_file=None, PSF_data_prefix="", sigSpin=0, psfRa=0.15, HocBuild=False, LOG_DEBUG=False): self.LOG_DEBUG = LOG_DEBUG @@ -223,7 +224,7 @@ class PSFInterp(PSFModel): self.iccd = int(chip.getChipLabel(chipID=chip.chipID)) # self.iccd = chip.chip_name - if PSF_data_file == None: + if PSF_data_file is None: print('Error - PSF_data_file is None') sys.exit() @@ -324,7 +325,7 @@ class PSFInterp(PSFModel): return twave return -1 - def get_PSF(self, chip, pos_img, bandpass, galsimGSObject=True, findNeighMode='treeFind', folding_threshold=5.e-3, pointing_pa=0.0): + def get_PSF(self, chip, pos_img, bandpass, galsimGSObject=True, findNeighMode='treeFind', folding_threshold=5.e-3, pointing_pa=0.0, extrapolate=False, ngg=2048): """ Get the PSF at a given image position @@ -358,20 +359,18 @@ class PSFInterp(PSFModel): imPSF = psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2, OnlyNeighbors=True, hoc=self.hoc[twave], hoclist=self.hoclist[twave], PSFCentroidWgt=True) - ''' - ############TEST: START - TestGaussian = False - if TestGaussian: - gsx = galsim.Gaussian(sigma=0.04) - #pointing_pa = -23.433333 - imPSF= gsx.shear(g1=0.8, g2=0.).rotate(0.*galsim.degrees).drawImage(nx = 256, ny=256, scale=pixSize).array - ############TEST: END - ''' + if extrapolate is True: + ccdList = [6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25] + rr_trim_list = [72, 64, 96, 88, 64, 72, 72, 76, 72, 72, 76, 72, 72, 64, 88, 96, 64, 72] + imPSF = psf_extrapolate(imPSF, rr_trim=rr_trim_list[ccdList.index(chip.chipID)], ngg=ngg) if galsimGSObject: - imPSFt = np.zeros([257, 257]) - imPSFt[0:256, 0:256] = imPSF - # imPSFt[120:130, 0:256] = 1. + if extrapolate is True: + imPSFt = np.zeros([ngg+1, ngg+1]) + imPSFt[:-1, :-1] = imPSF + else: + imPSFt = np.zeros([257, 257]) + imPSFt[0:256, 0:256] = imPSF img = galsim.ImageF(imPSFt, scale=pixSize) gsp = galsim.GSParams(folding_threshold=folding_threshold) @@ -402,7 +401,7 @@ class PSFInterp(PSFModel): Spinned PSF: g1, g2 and axis ratio 'a/b' """ a2Rad = np.pi/(60.0*60.0*180.0) - + ff = self.sigGauss * 0.107 * (1000.0/10.0) # in unit of [pixels] rc = np.sqrt(x*x + y*y) cpix = rc*(self.sigSpin*a2Rad) diff --git a/observation_sim/psf/PSFInterpSLS.py b/observation_sim/psf/PSFInterpSLS.py index 90ac027a4ce4d38a5fc93298d717b3b197e89351..b62cfedaec63fd6220f1cb097a9e8c30ecdd4b8c 100644 --- a/observation_sim/psf/PSFInterpSLS.py +++ b/observation_sim/psf/PSFInterpSLS.py @@ -23,6 +23,9 @@ from astropy.modeling.models import Gaussian2D from scipy import signal, interpolate import datetime import gc +from astropy.io import fits + +from observation_sim.psf._util import psf_extrapolate, psf_extrapolate1 # from jax import numpy as jnp LOG_DEBUG = False # ***# @@ -30,7 +33,7 @@ NPSF = 900 # ***# 30*30 PIX_SIZE_MICRON = 5. # ***# in microns -### find neighbors-KDtree### +# find neighbors-KDtree # def findNeighbors(tx, ty, px, py, dr=0.1, dn=1, OnlyDistance=True): # """ # find nearest neighbors by 2D-KDTree @@ -212,7 +215,7 @@ PIX_SIZE_MICRON = 5. # ***# in microns # return psfMaker -### define PSFInterp### +# define PSFInterp class PSFInterpSLS(PSFModel): def __init__(self, chip, filt, PSF_data_prefix="", sigSpin=0, psfRa=0.15, pix_size=0.005): if LOG_DEBUG: @@ -354,7 +357,7 @@ class PSFInterpSLS(PSFModel): convImg = convImg/np.sum(convImg) return convImg - def get_PSF(self, chip, pos_img_local=[1000, 1000], bandNo=1, galsimGSObject=True, folding_threshold=5.e-3, g_order='A', grating_split_pos=3685): + def get_PSF(self, chip, pos_img_local=[1000, 1000], bandNo=1, galsimGSObject=True, folding_threshold=5.e-3, g_order='A', grating_split_pos=3685, extrapolate=False, ngg=2048): """ Get the PSF at a given image position @@ -435,14 +438,27 @@ class PSFInterpSLS(PSFModel): # PSF_int_trans[ids_szero] = 0 # print(PSF_int_trans[ids_szero].shape[0],PSF_int_trans.shape) PSF_int_trans = PSF_int_trans/np.sum(PSF_int_trans) - ###DEBGU - ids_szero = PSF_int_trans<0 + PSF_int_trans = PSF_int_trans-np.min(PSF_int_trans) + PSF_int_trans = PSF_int_trans/np.sum(PSF_int_trans) + # fits.writeto('/home/zhangxin/CSST_SIM/CSST_sim_develop/psf_test/psf.fits',PSF_int_trans) + # DEBGU + ids_szero = PSF_int_trans < 0 n01 = PSF_int_trans[ids_szero].shape[0] n1 = np.sum(np.isinf(PSF_int_trans)) n2 = np.sum(np.isnan(PSF_int_trans)) - if n1>0 or n2>0: - print("DEBUG: PSFInterpSLS, inf:%d, nan:%d, 0 num:%d"%(n1, n2, n01)) + if n1 > 0 or n2 > 0: + print("DEBUG: PSFInterpSLS, inf:%d, nan:%d, 0 num:%d" % + (n1, n2, n01)) + if extrapolate is True: + # for rep_i in np.arange(0, 2, 1): + # PSF_int_trans[rep_i,:] = 1e9*pow(10,rep_i) + # PSF_int_trans[-1-rep_i,:] = 1e9*pow(10,rep_i) + # PSF_int_trans[:,rep_i] = 1e9*pow(10,rep_i) + # PSF_int_trans[:,-1-rep_i] = 1e9*pow(10,rep_i) + + PSF_int_trans = psf_extrapolate1(PSF_int_trans, ngg=ngg) + # fits.writeto('/home/zhangxin/CSST_SIM/CSST_sim_develop/psf_test/psf_large.fits',PSF_int_trans) #### # from astropy.io import fits @@ -537,36 +553,37 @@ class PSFInterpSLS(PSFModel): sumImg = np.sum(cutImg.array) tmp_img = cutImg*0 for j in np.arange(npc): - X_ = np.hstack((pos_p[:,1].flatten()[:, None], pos_p[:,0].flatten()[:, None]),dtype=np.float32) + X_ = np.hstack((pos_p[:, 1].flatten()[:, None], pos_p[:, 0].flatten()[ + :, None]), dtype=np.float32) Z_ = (pc_coeff[j].astype(np.float32)).flatten() # print(pc_coeff[j].shape[0], pos_p[:,1].shape[0], pos_p[:,0].shape[0]) cx_len = int(chip.npix_x) cy_len = int(chip.npix_y) - n_x = np.arange(0, cx_len, 1, dtype = int) - n_y = np.arange(0, cy_len, 1, dtype = int) + n_x = np.arange(0, cx_len, 1, dtype=int) + n_y = np.arange(0, cy_len, 1, dtype=int) M, N = np.meshgrid(n_x, n_y) # t1=datetime.datetime.now() # U = interpolate.griddata(X_, Z_, (M[0:cy_len, 0:cx_len],N[0:cy_len, 0:cx_len]), # method='nearest',fill_value=1.0) b_img = galsim.Image(cx_len, cy_len) - b_img.setOrigin(0,0) + b_img.setOrigin(0, 0) bounds = cutImg.bounds & b_img.bounds if bounds.area() == 0: continue # ys = cutImg.ymin - # if ys < 0: + # if ys < 0: # ys = 0 # ye = cutImg.ymin+cutImg.nrow - # if ye >= cy_len-1: + # if ye >= cy_len-1: # ye = cy_len-1 # if ye - ys <=0: # continue # xs = cutImg.xmin - # if xs < 0: + # if xs < 0: # xs = 0 # xe = cutImg.xmin+cutImg.ncol - # if xe >= cx_len-1: + # if xe >= cx_len-1: # xe = cx_len-1 # if xe - xs <=0: # continue @@ -574,10 +591,10 @@ class PSFInterpSLS(PSFModel): ye = bounds.ymax+1 xs = bounds.xmin xe = bounds.xmax+1 - U = interpolate.griddata(X_, Z_, (M[ys:ye, xs:xe],N[ys:ye, xs:xe]), - method='nearest',fill_value=1.0) + U = interpolate.griddata(X_, Z_, (M[ys:ye, xs:xe], N[ys:ye, xs:xe]), + method='nearest', fill_value=1.0) # t2=datetime.datetime.now() - + # print("time interpolate:", t2-t1) # if U.shape != cutImg.array.shape: @@ -586,53 +603,53 @@ class PSFInterpSLS(PSFModel): img_tmp = cutImg img_tmp[bounds] = img_tmp[bounds]*U psf = pcs[:, j].reshape(m_size, m_size) - tmp_img = tmp_img + signal.fftconvolve(img_tmp.array, psf, mode='same', axes=None) + tmp_img = tmp_img + \ + signal.fftconvolve(img_tmp.array, psf, mode='same', axes=None) # t3=datetime.datetime.now() # print("time convole:", t3-t2) del U del img_tmp - if np.sum(tmp_img.array)==0: + if np.sum(tmp_img.array) == 0: tmp_img = cutImg else: tmp_img = tmp_img/np.sum(tmp_img.array)*sumImg return tmp_img - def convolveFullImgWithPCAPSF(self, chip, folding_threshold=5.e-3): - keys_L1= chip_utils.getChipSLSGratingID(chip.chipID) + keys_L1 = chip_utils.getChipSLSGratingID(chip.chipID) # keys_L2 = ['order-2','order-1','order0','order1','order2'] - keys_L2 = ['order0','order1'] - keys_L3 = ['w1','w2','w3','w4'] + keys_L2 = ['order0', 'order1'] + keys_L3 = ['w1', 'w2', 'w3', 'w4'] npca = 10 x_start = chip.x_cen/chip.pix_size - chip.npix_x / 2. y_start = chip.y_cen/chip.pix_size - chip.npix_y / 2. - for i,gt in enumerate(keys_L1): + for i, gt in enumerate(keys_L1): psfCo = self.grating1_data if i > 0: psfCo = self.grating2_data for od in keys_L2: psfCo_L2 = psfCo['order1'] - if od in ['order-2','order-1','order0','order2']: + if od in ['order-2', 'order-1', 'order0', 'order2']: psfCo_L2 = psfCo['order0'] for w in keys_L3: img = chip.img_stack[gt][od][w] pcs = psfCo_L2['band'+w[1]]['band_data'][0].data - pos_p = psfCo_L2['band'+w[1]]['band_data'][1].data/chip.pix_size - np.array([y_start, x_start]) + pos_p = psfCo_L2['band'+w[1]]['band_data'][1].data / \ + chip.pix_size - np.array([y_start, x_start]) pc_coeff = psfCo_L2['band'+w[1]]['band_data'][2].data # print("DEBUG-----------",np.max(pos_p[:,1]),np.min(pos_p[:,1]), np.max(pos_p[:,0]),np.min(pos_p[:,0])) sum_img = np.sum(img.array) - - + # coeff_mat = np.zeros([npca, chip.npix_y, chip.npix_x]) # for m in np.arange(chip.npix_y): # for n in np.arange(chip.npix_x): # px = n # py = m - + # dist2 = (pos_p[:, 1] - px)*(pos_p[:, 1] - px) + (pos_p[:, 0] - py)*(pos_p[:, 0] - py) # temp_sort_dist = np.zeros([dist2.shape[0], 2]) # temp_sort_dist[:, 0] = np.arange(0, dist2.shape[0], 1) @@ -660,41 +677,45 @@ class PSFInterpSLS(PSFModel): # coeff_mat[:, m, n] = coeff_int m_size = int(pcs.shape[0]**0.5) - tmp_img = np.zeros_like(img.array,dtype=np.float32) + tmp_img = np.zeros_like(img.array, dtype=np.float32) for j in np.arange(npca): print(gt, od, w, j) - X_ = np.hstack((pos_p[:,1].flatten()[:, None], pos_p[:,0].flatten()[:, None]),dtype=np.float32) + X_ = np.hstack((pos_p[:, 1].flatten()[:, None], pos_p[:, 0].flatten()[ + :, None]), dtype=np.float32) Z_ = (pc_coeff[j].astype(np.float32)).flatten() # print(pc_coeff[j].shape[0], pos_p[:,1].shape[0], pos_p[:,0].shape[0]) sub_size = 4 cx_len = int(chip.npix_x/sub_size) cy_len = int(chip.npix_y/sub_size) - n_x = np.arange(0, chip.npix_x, sub_size, dtype = int) - n_y = np.arange(0, chip.npix_y, sub_size, dtype = int) + n_x = np.arange(0, chip.npix_x, sub_size, dtype=int) + n_y = np.arange(0, chip.npix_y, sub_size, dtype=int) M, N = np.meshgrid(n_x, n_y) - t1=datetime.datetime.now() + t1 = datetime.datetime.now() # U = interpolate.griddata(X_, Z_, (M[0:cy_len, 0:cx_len],N[0:cy_len, 0:cx_len]), # method='nearest',fill_value=1.0) U1 = interpolate.griddata(X_, Z_, (M, N), - method='nearest',fill_value=1.0) + method='nearest', fill_value=1.0) U = np.zeros_like(chip.img.array, dtype=np.float32) for mi in np.arange(cy_len): for mj in np.arange(cx_len): - U[mi*sub_size:(mi+1)*sub_size, mj*sub_size:(mj+1)*sub_size]=U1[mi,mj] - t2=datetime.datetime.now() - + U[mi*sub_size:(mi+1)*sub_size, mj * + sub_size:(mj+1)*sub_size] = U1[mi, mj] + t2 = datetime.datetime.now() + print("time interpolate:", t2-t1) img_tmp = img.array*U psf = pcs[:, j].reshape(m_size, m_size) - tmp_img = tmp_img + signal.fftconvolve(img_tmp, psf, mode='same', axes=None) + tmp_img = tmp_img + \ + signal.fftconvolve( + img_tmp, psf, mode='same', axes=None) - t3=datetime.datetime.now() + t3 = datetime.datetime.now() print("time convole:", t3-t2) del U del U1 - + chip.img = chip.img + tmp_img*sum_img/np.sum(tmp_img) del tmp_img gc.collect() diff --git a/observation_sim/psf/__init__.py b/observation_sim/psf/__init__.py index 80bf7d3685fc1aba1b88aa8a4535163ca39d55fa..43e878cdcabc19993b6f9c9c572b725b214daf50 100755 --- a/observation_sim/psf/__init__.py +++ b/observation_sim/psf/__init__.py @@ -3,4 +3,4 @@ from .PSFGauss import PSFGauss # from .PSFInterp.PSFInterp import PSFInterp from .PSFInterp import PSFInterp from .PSFInterpSLS import PSFInterpSLS -from .FieldDistortion import FieldDistortion \ No newline at end of file +from .FieldDistortion import FieldDistortion diff --git a/observation_sim/psf/_util.py b/observation_sim/psf/_util.py new file mode 100644 index 0000000000000000000000000000000000000000..1b7aa102c8f49dd26ddcf3aabb7e3056ac1b03b4 --- /dev/null +++ b/observation_sim/psf/_util.py @@ -0,0 +1,120 @@ +import numpy as np +from scipy.interpolate import interp1d + + +def binningPSF(img, ngg): + imgX = img.reshape(ngg, img.shape[0]//ngg, ngg, img.shape[1]//ngg).mean(-1).mean(1) + return imgX + + +def radial_average_at_pixel(image, center_x, center_y, dr=10): + # Get coordinates relative to the specified center pixel (x, y) + y, x = np.indices(image.shape) + r = np.sqrt((x - center_x)**2 + (y - center_y)**2) + + # Set up bins + max_radius = int(r.max()) # Maximum distance from the center pixel + radial_bins = np.arange(0, max_radius, dr) + + # Compute average value in each bin + radial_means = [] + for i in range(len(radial_bins) - 1): + mask = (r >= radial_bins[i]) & (r < radial_bins[i + 1]) + if np.any(mask): + radial_means.append(image[mask].mean()) + else: + radial_means.append(0) # In case no pixels are in the bin + return radial_bins[:-1], radial_means # Exclude last bin since no mean calculated + + +def psf_extrapolate(psf, rr_trim=64, ngg=256): + # ngg = 256 + # extrapolate PSF + if True: + xim = np.arange(256)-128 + xim, yim = np.meshgrid(xim, xim) + rim = np.sqrt(xim**2 + yim**2) + + # rr_trim = 96 + psf_temp = psf + psf_temp[rim > rr_trim] = 0 + radii, means = radial_average_at_pixel(psf_temp, 128, 128, dr=4) + + radii_log = np.log(radii[1:]) + means_log = np.log(means[1:]) + + finite_mask = np.isfinite(means_log) + f_interp = interp1d(radii_log[finite_mask][:-1], means_log[finite_mask][:-1], kind='linear', fill_value="extrapolate") + + # ngg = 1024 + xim = np.arange(ngg)-int(ngg/2) + xim, yim = np.meshgrid(xim, xim) + rim = np.sqrt(xim**2 + yim**2) + rim[int(ngg/2), int(ngg/2)] = np.finfo(float).eps # 1e-7 + rim_log = np.log(rim) + y_new = f_interp(rim_log) + + arr = np.zeros([ngg, ngg]) + arr[int(ngg/2-128):int(ngg/2+128), int(ngg/2-128):int(ngg/2+128)] = np.log(psf_temp + np.finfo(float).eps) + arr[rim > rr_trim] = 0 + arr[arr == 0] = y_new[arr == 0] + psf = np.exp(arr) + psf[rim > int(ngg/2)] = 0 + imPSF = psf # binningPSF(psf, int(ngg/2)) + imPSF = imPSF/np.nansum(imPSF) + return imPSF + + +def psf_extrapolate1(psf, rr_trim=64, ngg=256): + # ngg = 256 + # extrapolate PSF + if True: + psf_enlar = np.zeros([ngg, ngg]) + psf_enlar[int(ngg/2-128):int(ngg/2+128), int(ngg/2-128):int(ngg/2+128)] = psf + xim = np.arange(ngg)-ngg/2 + xim, yim = np.meshgrid(xim, xim) + rim = np.sqrt(xim**2 + yim**2) + + psf_temp = psf_enlar + # psf_temp[rim >= rr_trim] = 0 + psf_temp[rim >= ngg/2-2] = np.finfo(float).eps + radii, means = radial_average_at_pixel(psf_temp, ngg/2, ngg/2, dr=2) + + radii_log = np.log(radii[1:]) + # radii_log = radii[1:] + means_log = np.log(means[1:]) + + # xim = np.arange(256)-128 + # xim, yim = np.meshgrid(xim, xim) + # rim = np.sqrt(xim**2 + yim**2) + + # # rr_trim = 96 + # psf_temp = psf + # psf_temp[rim > rr_trim] = 0 + # radii, means = radial_average_at_pixel(psf_temp, 128, 128, dr=4) + + # radii_log = np.log10(radii[1:]) + # # radii_log = radii[1:] + # means_log = np.log10(means[1:]) + + finite_mask = np.isfinite(means_log) + f_interp = interp1d(radii_log[finite_mask][:-1], means_log[finite_mask][:-1], kind='linear', fill_value="extrapolate") + + # ngg = 1024 + # xim = np.arange(ngg)-int(ngg/2) + # xim, yim = np.meshgrid(xim, xim) + # rim = np.sqrt(xim**2 + yim**2) + # rim[int(ngg/2), int(ngg/2)] = np.finfo(float).eps # 1e-7 + rim_log = np.log(rim) + y_new = f_interp(rim_log) + + arr = np.zeros([ngg, ngg]) + arr[int(ngg/2-128):int(ngg/2+128), int(ngg/2-128):int(ngg/2+128)] = np.log(psf + np.finfo(float).eps) + arr[rim > 128-2] = 0 + arr[arr == 0] = y_new[arr == 0] + psf_n = np.exp(arr) + # psf_n[int(ngg/2-128):int(ngg/2+128), int(ngg/2-128):int(ngg/2+128)] = psf + # psf[rim > int(ngg/2)] = 0 + imPSF = psf_n # binningPSF(psf, int(ngg/2)) + # imPSF = imPSF/np.nansum(imPSF) + return imPSF diff --git a/observation_sim/sim_steps/__init__.py b/observation_sim/sim_steps/__init__.py index e8e2661c7d92f787e27fb0216bfd13131a70b976..7669fe45bae94f3ed309840911e1d9c8660d89e7 100644 --- a/observation_sim/sim_steps/__init__.py +++ b/observation_sim/sim_steps/__init__.py @@ -11,11 +11,11 @@ class SimSteps: from .prepare_headers import prepare_headers, updateHeaderInfo from .add_sky_background import add_sky_background_sci, add_sky_flat_calibration, add_sky_background - from .add_objects import add_objects + from .add_objects import add_objects, _is_obj_valid from .add_cosmic_rays import add_cosmic_rays from .add_pattern_noise import apply_PRNU, add_poisson_and_dark, add_detector_defects, add_nonlinearity, add_blooming, add_bias from .add_brighter_fatter_CTE import add_brighter_fatter, apply_CTE - from .readout_output import add_prescan_overscan, add_readout_noise, apply_gain, quantization_and_output + from .readout_output import add_prescan_overscan, add_readout_noise, apply_gain, quantization_and_output, add_crosstalk from .add_LED_flat import add_LED_Flat @@ -37,4 +37,5 @@ SIM_STEP_TYPES = { "quantization_and_output": "quantization_and_output", "led_calib_model": "add_LED_Flat", "sky_flatField": "add_sky_flat_calibration", + "cross_talk": "add_crosstalk" } diff --git a/observation_sim/sim_steps/add_LED_flat.py b/observation_sim/sim_steps/add_LED_flat.py index 76bcdbd5459fdf0eb7e41bc652acc36e48f2fcb5..7c5683c9478846afb652b9c3276c012ee94e9a11 100644 --- a/observation_sim/sim_steps/add_LED_flat.py +++ b/observation_sim/sim_steps/add_LED_flat.py @@ -27,7 +27,7 @@ def add_LED_Flat(self, chip, filt, tel, pointing, catalog, obs_param): self.updateHeaderInfo(header_flag='ext', keys=['LEDT01', 'LEDT02', 'LEDT03', 'LEDT04', 'LEDT05', 'LEDT06', 'LEDT07', 'LEDT08', 'LEDT09', 'LEDT10', 'LEDT11', 'LEDT12', 'LEDT13', 'LEDT14'], values=letts) - if obs_param["shutter_effect"] == True: + if obs_param["shutter_effect"] is True: pf_map = pf_map * chip.shutter_img pf_map = np.array(pf_map, dtype='float32') self.updateHeaderInfo(header_flag='ext', keys=[ diff --git a/observation_sim/sim_steps/add_objects.py b/observation_sim/sim_steps/add_objects.py index 6c9c7c197577b2c8a3fda456043366b03d40691e..06bd712698c364bc54035a60ce9f27264acf30fd 100644 --- a/observation_sim/sim_steps/add_objects.py +++ b/observation_sim/sim_steps/add_objects.py @@ -11,6 +11,24 @@ from astropy.time import Time from datetime import datetime, timezone +def _is_obj_valid(self, obj): + if obj.param['star'] == 4: + # Currently there's no parameter checks for 'calib' type + return True + pos_keys = ['ra', 'dec'] + shape_keys = ['hlr_bulge', 'hlr_disk', + 'e1_disk', 'e2_disk', 'e1_bulge', 'e2_bulge'] + if any(obj.param[key] == -999. for key in pos_keys): + msg = 'One or more positional information (ra, dec) is missing' + self.chip_output.Log_error(msg) + return False + if obj.param['star'] == 0 and any(obj.param[key] == -999. for key in shape_keys): + msg = 'One or more shape information (hlr_bulge, hlr_disk, e1_disk, e2_disk, e1_bulge, e2_bulge) is missing' + self.chip_output.Log_error(msg) + return False + return True + + def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): # Get exposure time @@ -48,7 +66,7 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): self.chip_output.Log_error("unrecognized PSF model type!!", flush=True) # Apply field distortion model - if obs_param["field_dist"] == True: + if obs_param["field_dist"] is True: fd_model = FieldDistortion(chip=chip, img_rot=pointing.img_pa.deg) else: fd_model = None @@ -58,7 +76,10 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): 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) + exptime=pointing.exp_time, + full_depth_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 @@ -83,12 +104,17 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): # # [DEBUG] [TODO] # if j >= 10: # break + obj = cat.objs[j] + if not self._is_obj_valid(obj): + continue + # load and convert SED; also caculate object's magnitude in all CSST bands try: sed_data = cat.load_sed(obj) norm_filt = cat.load_norm_filt(obj) + obj.sed, obj.param["mag_%s" % filt.filter_type.lower()], obj.param["flux_%s" % filt.filter_type.lower()] = cat.convert_sed( mag=obj.param["mag_use_normal"], sed=sed_data, @@ -96,11 +122,12 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): norm_filt=norm_filt, mu=obj.mu ) + _, obj.param["mag_%s" % cut_filter.filter_type.lower()], obj.param["flux_%s" % cut_filter.filter_type.lower()] = cat.convert_sed( mag=obj.param["mag_use_normal"], sed=sed_data, target_filt=cut_filter, - norm_filt=norm_filt, + norm_filt=(norm_filt if norm_filt else filt), mu=obj.mu ) except Exception as e: @@ -116,7 +143,7 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): 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" % ( + self.chip_output.Log_info("obj %s too bright!! 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() @@ -150,10 +177,11 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): # [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: + # if pos_img.x == -1 or pos_img.y == -1: + if pos_img is None: 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)) + self.chip_output.Log_error("Object missed: %s" % (obj.id)) missed_obj += 1 obj.unload_SED() continue @@ -199,7 +227,7 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): pass elif isUpdated == 0: missed_obj += 1 - self.chip_output.Log_error("Objected missed: %s" % (obj.id)) + self.chip_output.Log_error("Object missed: %s" % (obj.id)) else: self.chip_output.Log_error( "Draw error, object omitted: %s" % (obj.id)) @@ -213,11 +241,12 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): self.chip_output.Log_error("obj id: %s" % (obj.param['id'])) self.chip_output.Log_error(" e1: %.5f\n e2: %.5f\n size: %f\n bfrac: %f\n detA: %f\n g1: %.5f\n g2: %.5f\n" % ( obj.param['e1'], obj.param['e2'], obj.param['size'], obj.param['bfrac'], obj.param['detA'], obj.param['g1'], obj.param['g2'])) - # Unload SED: obj.unload_SED() del obj # gc.collect() + cat.free_mem() + del cat if chip.survey_type == "spectroscopic" and not self.overall_config["run_option"]["out_cat_only"] and chip.slsPSFOptim: # from observation_sim.instruments.chip import chip_utils as chip_utils @@ -253,10 +282,10 @@ def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): # Apply flat fielding (with shutter effects) flat_normal = np.ones_like(chip.img.array) - if obs_param["flat_fielding"] == True: + if obs_param["flat_fielding"] is True: flat_normal = flat_normal * chip.flat_img.array / \ np.mean(chip.flat_img.array) - if obs_param["shutter_effect"] == True: + if obs_param["shutter_effect"] is True: flat_normal = flat_normal * chip.shutter_img flat_normal = np.array(flat_normal, dtype='float32') self.updateHeaderInfo(header_flag='ext', keys=[ diff --git a/observation_sim/sim_steps/add_pattern_noise.py b/observation_sim/sim_steps/add_pattern_noise.py index f92cf36be89a62dfbb642a4643a8a4ea49a1b6e5..aa8d61a911a4f515fe195de1240cddcb5ef790ae 100644 --- a/observation_sim/sim_steps/add_pattern_noise.py +++ b/observation_sim/sim_steps/add_pattern_noise.py @@ -5,7 +5,7 @@ from observation_sim.instruments.chip import effects def apply_PRNU(self, chip, filt, tel, pointing, catalog, obs_param): chip.img *= chip.prnu_img - if self.overall_config["output_setting"]["prnu_output"] == True: + if self.overall_config["output_setting"]["prnu_output"] is True: chip.prnu_img.write("%s/FlatImg_PRNU_%s.fits" % (self.chip_output.subdir, str(chip.chipID).rjust(2, '0'))) return chip, filt, tel, pointing @@ -19,10 +19,10 @@ def add_poisson_and_dark(self, chip, filt, tel, pointing, catalog, obs_param): else: exptime = pointing.exp_time - if obs_param["add_dark"] == True: + if obs_param["add_dark"] is True: chip.img, _ = chip_utils.add_poisson(img=chip.img, chip=chip, - exptime=pointing.exp_time, + exptime=exptime, poisson_noise=chip.poisson_noise, InputDark=None) else: @@ -46,7 +46,7 @@ def add_detector_defects(self, chip, filt, tel, pointing, catalog, obs_param): fraction=badfraction, seed=self.overall_config["random_seeds"]["seed_defective"]+chip.chipID, biaslevel=0) # Apply Bad columns - if obs_param["bad_columns"] == True: + if obs_param["bad_columns"] is True: chip.img = effects.BadColumns(chip.img, seed=self.overall_config["random_seeds"]["seed_badcolumns"], chipid=chip.chipID) @@ -73,13 +73,13 @@ def add_blooming(self, chip, filt, tel, pointing, catalog, obs_param): 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: + if obs_param["bias_16channel"] is 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: + elif obs_param["bias_16channel"] is False: chip.img += chip.bias_level return chip, filt, tel, pointing diff --git a/observation_sim/sim_steps/add_sky_background.py b/observation_sim/sim_steps/add_sky_background.py index ab013f34b12bca3c8b88144d96bc56c548a372b4..082e3aa50f3b87f74b20b5f8640538e70834c119 100644 --- a/observation_sim/sim_steps/add_sky_background.py +++ b/observation_sim/sim_steps/add_sky_background.py @@ -16,10 +16,10 @@ def add_sky_background_sci(self, chip, filt, tel, pointing, catalog, obs_param): exptime = pointing.exp_time flat_normal = np.ones_like(chip.img.array) - if obs_param["flat_fielding"] == True: + if obs_param["flat_fielding"] is True: flat_normal = flat_normal * chip.flat_img.array / \ np.mean(chip.flat_img.array) - if obs_param["shutter_effect"] == True: + if obs_param["shutter_effect"] is True: flat_normal = flat_normal * chip.shutter_img flat_normal = np.array(flat_normal, dtype='float32') self.updateHeaderInfo(header_flag='ext', keys=[ @@ -81,14 +81,14 @@ def add_sky_flat_calibration(self, chip, filt, tel, pointing, catalog, obs_param norm_scaler = skyback_level/exptime / filter_param.param[sky_level_filt][5] flat_normal = np.ones_like(chip.img.array) - if obs_param["flat_fielding"] == True: + if obs_param["flat_fielding"] is True: flat_normal = flat_normal * chip.flat_img.array / \ np.mean(chip.flat_img.array) - if obs_param["shutter_effect"] == True: + if obs_param["shutter_effect"] is True: flat_normal = flat_normal * chip.shutter_img flat_normal = np.array(flat_normal, dtype='float32') # output 16-bit shutter effect image with pixel value <=65535 - if self.overall_config["output_setting"]["shutter_output"] == True: + if self.overall_config["output_setting"]["shutter_output"] is True: shutt_gsimg = galsim.ImageUS(chip.shutter_img*6E4) shutt_gsimg.write("%s/ShutterEffect_%s_1.fits" % (self.chip_output.subdir, str(chip.chipID).rjust(2, '0'))) @@ -103,11 +103,11 @@ def add_sky_flat_calibration(self, chip, filt, tel, pointing, catalog, obs_param filter_param.param[chip.filter_type][5] / tel.pupil_area * exptime elif chip.survey_type == "spectroscopic": # flat_normal = np.ones_like(chip.img.array) - if obs_param["flat_fielding"] == True: + if obs_param["flat_fielding"] is True: flat_normal = flat_normal * chip.flat_img.array / \ np.mean(chip.flat_img.array) - if obs_param["shutter_effect"] == True: + if obs_param["shutter_effect"] is True: flat_normal = flat_normal * chip.shutter_img flat_normal = np.array(flat_normal, dtype='float32') diff --git a/observation_sim/sim_steps/prepare_headers.py b/observation_sim/sim_steps/prepare_headers.py index e8c41741710bd2c24512aa3e73a0ff7c1edf24d5..0a02595f55be5d269bcd55161a9ffcf60343b6b1 100644 --- a/observation_sim/sim_steps/prepare_headers.py +++ b/observation_sim/sim_steps/prepare_headers.py @@ -17,7 +17,9 @@ def prepare_headers(self, chip, pointing): 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')) + chip_name=str(chip.chipID).rjust(2, '0'), + obstype=pointing.survey_field_type, + dataset=pointing.dataset) self.h_ext = generateExtensionHeader( chip=chip, xlen=chip.npix_x, diff --git a/observation_sim/sim_steps/readout_output.py b/observation_sim/sim_steps/readout_output.py index 0f3f4f5a227ff39ec81764929b8f324a73123298..f21461d56abd943505f5746f551ec470ed549525 100644 --- a/observation_sim/sim_steps/readout_output.py +++ b/observation_sim/sim_steps/readout_output.py @@ -17,13 +17,52 @@ def add_prescan_overscan(self, chip, filt, tel, pointing, catalog, obs_param): over1=chip.overscan_x, over2=chip.overscan_y) - if obs_param["add_dark"] == True: + if obs_param["add_dark"] is True: ny = int(chip.npix_y/2) base_dark = (ny-1)*(chip.readout_time/ny)*chip.dark_noise chip.img.array[(chip.prescan_y+ny):-(chip.prescan_y+ny), :] = base_dark return chip, filt, tel, pointing +def add_crosstalk(self, chip, filt, tel, pointing, catalog, obs_param): + crosstalk = np.zeros([16, 16]) + crosstalk[0, :] = np.array([1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[1, :] = np.array([1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[2, :] = np.array([0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[3, :] = np.array([0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[4, :] = np.array([0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[5, :] = np.array([0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[6, :] = np.array([0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[7, :] = np.array([0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[8, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0.]) + crosstalk[9, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0.]) + crosstalk[10, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0.]) + crosstalk[11, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0.]) + crosstalk[12, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0.]) + crosstalk[13, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0.]) + crosstalk[14, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4]) + crosstalk[15, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1.]) + + # 2*8 -> 1*16 + img = chip_utils.formatOutput(chip.img) + ny, nx = img.array.shape + nsecy = 1 + nsecx = 16 + dy = int(ny/nsecy) + dx = int(nx/nsecx) + + newimg = galsim.Image(nx, ny, init_value=0) + for i in range(16): + for j in range(16): + newimg.array[:, int(i*dx):int(i*dx+dx)] += crosstalk[i, j]*img.array[:, int(j*dx):int(j*dx+dx)] + + # 1*16 -> 2*8 + newimg = chip_utils.formatRevert(newimg) + chip.img.array[:, :] = newimg.array[:, :] + + 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 @@ -36,13 +75,13 @@ def add_readout_noise(self, chip, filt, tel, pointing, catalog, obs_param): def apply_gain(self, chip, filt, tel, pointing, catalog, obs_param): self.chip_output.Log_info(" Applying Gain") - if obs_param["gain_16channel"] == True: + if obs_param["gain_16channel"] is True: chip.img, chip.gain_channel = effects.ApplyGainNonUniform16(chip.img, gain=chip.gain, nsecy=chip.nsecy, nsecx=chip.nsecx, seed=self.overall_config["random_seeds"]["seed_gainNonUniform"]+chip.chipID) - elif obs_param["gain_16channel"] == False: + elif obs_param["gain_16channel"] is False: chip.img /= chip.gain chip.gain_channel = np.ones(chip.nsecy*chip.nsecx)*chip.gain return chip, filt, tel, pointing @@ -74,7 +113,7 @@ def quantization_and_output(self, chip, filt, tel, pointing, catalog, obs_param) self.updateHeaderInfo(header_flag='ext', keys=['GAIN01', 'GAIN02', 'GAIN03', 'GAIN04', 'GAIN05', 'GAIN06', 'GAIN07', 'GAIN08', 'GAIN09', 'GAIN10', 'GAIN11', 'GAIN12', 'GAIN13', 'GAIN14', 'GAIN15', 'GAIN16'], values=gains) - if obs_param["format_output"] == True: + if obs_param["format_output"] is True: self.chip_output.Log_info(" Apply 1*16 format") chip.img = chip_utils.formatOutput(GSImage=chip.img) chip.nsecy = 1 diff --git a/run_sim.py b/run_sim.py index f1179851f87e066259bbc9cbd1e63939da78e3fc..416cb295bc8afefdaa3bd3527f2fe87041d0801d 100755 --- a/run_sim.py +++ b/run_sim.py @@ -18,7 +18,7 @@ def run_sim(): Parameters ---------- Catalog : Class - a catalog class which is inherited from observation_sim.mock_objects.CatalogBase + a catalog class which is inherited from observation_sim.mock_objects.CatalogBase Returns ---------- @@ -63,6 +63,9 @@ def run_sim(): if "run_counter" not in config: config["run_counter"] = 0 + if "data_set" not in config: + config["data_set"] = "csst-msc" + # Generate lists pointings based on the input pointing list (or default # pointing RA, DEC) and "config["obs_setting"]["run_pointings"]". # "config['obs_setting']['np_cal']"" is the number of CAL pointings which will be @@ -72,7 +75,7 @@ def run_sim(): if "pointing_dir" in config['obs_setting']: pointing_dir = config['obs_setting']["pointing_dir"] pointing_list = generate_pointing_list( - config=config, pointing_filename=config['obs_setting']['pointing_file'], data_dir=pointing_dir) + config=config, pointing_filename=config['obs_setting']['pointing_file'], data_dir=pointing_dir, dataset=config["data_set"]) # Make the main output directories run_dir = make_run_dirs( diff --git a/tests/test_BF_CTE.py b/tests/test_BF_CTE.py index 26b9dad3912ec479b1bba258e4b9433bffa99b4c..a37bf39cc8d42e9734ea67e8799a92966cef23a8 100644 --- a/tests/test_BF_CTE.py +++ b/tests/test_BF_CTE.py @@ -22,7 +22,7 @@ except ImportError: import importlib_resources as pkg_resources -### test FUNCTION --- START ### +# test FUNCTION --- START # def add_brighter_fatter(img): # Inital dynamic lib try: @@ -50,7 +50,7 @@ def add_brighter_fatter(img): img.array[:, :] = np.reshape(arr_imc, [nx, ny]) del arr_ima, arr_imc return img -### test FUNCTION --- END ### +# test FUNCTION --- END # def defineCCD(iccd, config_file): diff --git a/tests/test_PSFmodule.py b/tests/test_PSFmodule.py index a977dd9960377866e86168452e2c9930ba0a6b52..434c2ec3de4366a8b9131b84003cf2219604ffc8 100644 --- a/tests/test_PSFmodule.py +++ b/tests/test_PSFmodule.py @@ -72,7 +72,7 @@ class PSFInterpModule_coverage(unittest.TestCase): psfB = psfModel.get_PSF( chip=chip, pos_img=pos_img, findNeighMode='hoclistFind', bandpass=bandpass[0], galsimGSObject=False) - self.assertTrue(psf != None) + self.assertTrue(psf is not None) self.assertTrue(np.max(np.abs(psfA-psfB)) < 1e-6) diff --git a/tests/test_SpecDisperse.py b/tests/test_SpecDisperse.py index 4a6553eae3e617f19c173bad1ec61f2c475326f2..df7a7801ff447661e94f6e6aa243d96e3b41abd8 100644 --- a/tests/test_SpecDisperse.py +++ b/tests/test_SpecDisperse.py @@ -428,8 +428,8 @@ class TestSpecDisperse(unittest.TestCase): plt.xlim(6200, 10000) plt.ylim(1, 4) plt.yscale('log') - plt.xlabel('$\lambda$') - plt.ylabel('$F\lambda$') + plt.xlabel(r'$\lambda$') + plt.ylabel(r'$F\lambda$') plt.legend(['one spec', 'split in 8000 A']) plt.show() diff --git a/tests/test_Straylight.py b/tests/test_Straylight.py index 90f82a98a85da31fae83b1096be593d4634b1047..63c1376a3a610d5f2fd3c9f11157b93dc9e5ede7 100644 --- a/tests/test_Straylight.py +++ b/tests/test_Straylight.py @@ -192,7 +192,7 @@ class TestStraylight(unittest.TestCase): plt.figure() plt.plot(spec['WAVELENGTH'], spec['FLUX'], 'r') plt.xlabel('WAVELENGTH') - plt.ylabel('F$\lambda$(erg/s/cm2/A/arcsec2)') + plt.ylabel(r'F$\lambda$(erg/s/cm2/A/arcsec2)') plt.xlim(2000, 10000) plt.show() median = np.median(sl_e_pix[0:tnum]) diff --git a/tests/test_crosstalk.py b/tests/test_crosstalk.py new file mode 100644 index 0000000000000000000000000000000000000000..bafe9fdc0c351e7f24d1343e1ddca4586e1fbc59 --- /dev/null +++ b/tests/test_crosstalk.py @@ -0,0 +1,100 @@ +import unittest + +import sys +import os +import math +from itertools import islice +import numpy as np +import copy +import ctypes +import galsim +import yaml +from astropy.io import fits + +from observation_sim.instruments.chip import chip_utils +import matplotlib.pyplot as plt + + +# test FUNCTION --- START # +def add_crosstalk(GSimg): + crosstalk = np.zeros([16, 16]) + crosstalk[0, :] = np.array([1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[1, :] = np.array([1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[2, :] = np.array([0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[3, :] = np.array([0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[4, :] = np.array([0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[5, :] = np.array([0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[6, :] = np.array([0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[7, :] = np.array([0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0., 0., 0.]) + crosstalk[8, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0., 0., 0.]) + crosstalk[9, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0., 0., 0.]) + crosstalk[10, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0., 0., 0.]) + crosstalk[11, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0., 0., 0.]) + crosstalk[12, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4, 0., 0.]) + crosstalk[13, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1., 0., 0.]) + crosstalk[14, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1e-4]) + crosstalk[15, :] = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1e-4, 1.]) + + # 2*8 -> 1*16 + img = chip_utils.formatOutput(GSimg) + ny, nx = img.array.shape + nsecy = 1 + nsecx = 16 + dy = int(ny/nsecy) + dx = int(nx/nsecx) + + newimg = galsim.Image(nx, ny, init_value=0) + for i in range(16): + for j in range(16): + newimg.array[:, int(i*dx):int(i*dx+dx)] += crosstalk[i, j]*img.array[:, int(j*dx):int(j*dx+dx)] + + # 1*16 -> 2*8 + newimg = chip_utils.formatRevert(newimg) + + return newimg +# test FUNCTION --- END # + + +class detModule_coverage(unittest.TestCase): + def __init__(self, methodName='runTest'): + super(detModule_coverage, self).__init__(methodName) + self.dataPath = os.path.join( + os.getenv('UNIT_TEST_DATA_ROOT'), 'csst_msc_sim/csst_fz_msc') + + def test_add_crosstalk(self): + nsecy = 2 + nsecx = 8 + ny, nx = 1024, 1024 + dy = int(ny/nsecy) + dx = int(nx/nsecx) + mapclip = np.zeros([dy, int(nsecx*nsecy*dx)]) + for i in range(int(nsecx*nsecy)): + mapclip[:, i*dx:dx+i*dx] = np.random.randint(10)+np.random.rand(int(dy*dx)).reshape([dy, dx]) + mapclip = galsim.ImageF(mapclip) + + nsecy = 1 + nsecx = 16 + ny, nx = mapclip.array.shape + dy = int(ny/nsecy) + dx = int(nx/nsecx) + for i in range(int(nsecy*nsecx)): + gal = galsim.Gaussian(sigma=0.2, flux=500000).drawImage(nx=32, ny=32).array + py = np.random.randint(450)+10 + mapclip.array[py:py+32, int(i*dx)+10:int(i*dx)+42] += gal + + tmap = chip_utils.formatRevert(mapclip, nsecy=1, nsecx=16) # 1*16 -> 2*8 + temp = add_crosstalk(tmap) + + fig = plt.figure(figsize=(20, 60)) + ax = plt.subplot(311) + plt.imshow(np.log10(mapclip.array+1), origin='lower', cmap='gray') + ax = plt.subplot(312) + plt.imshow(np.log10(temp.array+1), origin='lower', cmap='gray') + ax = plt.subplot(313) + plt.imshow(np.log10(temp.array-tmap.array+1), origin='lower', cmap='gray') + plt.savefig(os.path.join(self.dataPath, "./test_crosstalk.png"), dpi=300, bbox_inches='tight') + self.assertTrue(True) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_darknoise_func.py b/tests/test_darknoise_func.py index 42bf398a5b4d5114f67377aff8f45931843e191d..58afd458d6a562d1ce19c0031629d11812a41ec8 100644 --- a/tests/test_darknoise_func.py +++ b/tests/test_darknoise_func.py @@ -10,11 +10,11 @@ import yaml from observation_sim.instruments import Chip, Filter, FilterParam, FocalPlane -### test FUNCTION --- START ### +# test FUNCTION --- START ### def get_base_img(img, chip, read_noise, readout_time, dark_noise, exptime=150., InputDark=None): - if InputDark == None: + if InputDark is None: # base_level = read_noise**2 + dark_noise*(exptime+0.5*readout_time) # base_level = dark_noise*(exptime+0.5*readout_time) base_level = dark_noise*(exptime) @@ -31,7 +31,7 @@ def get_base_img(img, chip, read_noise, readout_time, dark_noise, exptime=150., base_img2[ny:, :] = arr[::-1, :] base_img2[:, :] = base_img2[:, :]*(readout_time/ny)*dark_noise return base_img1+base_img2 -### test FUNCTION --- END ### +# test FUNCTION --- END ### def defineCCD(iccd, config_file): diff --git a/tests/test_imaging.py b/tests/test_imaging.py index 6fa0bca26068c2f52e459a15775f9038c7a1cbd3..5ba5cdc67d4b4d8b272a03d4a530f8e4905ff8c4 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -209,7 +209,7 @@ class imagingModule_coverage(unittest.TestCase): gal = gal + gal_temp print(gal) - self.assertTrue(gal != None) + self.assertTrue(gal is not None) if __name__ == '__main__': diff --git a/tests/test_prescan_overscan_func.py b/tests/test_prescan_overscan_func.py index b1a11a345646204603da70045c1bda9c04b4254a..01ec75ffd7bc66e721387256a88add2458ea982b 100644 --- a/tests/test_prescan_overscan_func.py +++ b/tests/test_prescan_overscan_func.py @@ -1,6 +1,8 @@ import unittest -import sys,os,math +import sys +import os +import math from itertools import islice import numpy as np import galsim @@ -8,14 +10,15 @@ import yaml from observation_sim.instruments import Chip, Filter, FilterParam, FocalPlane -### test FUNCTION --- START ### -def AddPreScan(GSImage, pre1=27, pre2=4, over1=71, over2=80, nsecy = 2, nsecx=8): - img= GSImage.array + +# test FUNCTION --- START # +def AddPreScan(GSImage, pre1=27, pre2=4, over1=71, over2=80, nsecy=2, nsecx=8): + img = GSImage.array ny, nx = img.shape dx = int(nx/nsecx) dy = int(ny/nsecy) - imgt=np.zeros([int(nsecy*nsecx), int(ny/nsecy+pre2+over2), int(nx/nsecx+pre1+over1)]) + imgt = np.zeros([int(nsecy*nsecx), int(ny/nsecy+pre2+over2), int(nx/nsecx+pre1+over1)]) for iy in range(nsecy): for ix in range(nsecx): if iy % 2 == 0: @@ -23,7 +26,7 @@ def AddPreScan(GSImage, pre1=27, pre2=4, over1=71, over2=80, nsecy = 2, nsecx=8) else: tx = (nsecx-1)-ix ty = iy - chunkidx = int(tx+ty*nsecx) #chunk1-[1,2,3,4], chunk2-[5,6,7,8], chunk3-[9,10,11,12], chunk4-[13,14,15,16] + chunkidx = int(tx+ty*nsecx) # chunk1-[1,2,3,4], chunk2-[5,6,7,8], chunk3-[9,10,11,12], chunk4-[13,14,15,16] imgtemp = np.zeros([int(ny/nsecy+pre2+over2), int(nx/nsecx+pre1+over1)]) if int(chunkidx/4) == 0: @@ -31,40 +34,41 @@ def AddPreScan(GSImage, pre1=27, pre2=4, over1=71, over2=80, nsecy = 2, nsecx=8) imgt[chunkidx, :, :] = imgtemp if int(chunkidx/4) == 1: imgtemp[pre2:pre2+dy, over1:over1+dx] = img[iy*dy:(iy+1)*dy, ix*dx:(ix+1)*dx] - imgt[chunkidx, :, :] = imgtemp #[:, ::-1] + imgt[chunkidx, :, :] = imgtemp # [:, ::-1] if int(chunkidx/4) == 2: imgtemp[over2:over2+dy, over1:over1+dx] = img[iy*dy:(iy+1)*dy, ix*dx:(ix+1)*dx] - imgt[chunkidx, :, :] = imgtemp #[::-1, ::-1] + imgt[chunkidx, :, :] = imgtemp # [::-1, ::-1] if int(chunkidx/4) == 3: imgtemp[over2:over2+dy, pre1:pre1+dx] = img[iy*dy:(iy+1)*dy, ix*dx:(ix+1)*dx] - imgt[chunkidx, :, :] = imgtemp #[::-1, :] + imgt[chunkidx, :, :] = imgtemp # [::-1, :] - imgtx1 = np.hstack(imgt[:nsecx:, :, :]) #hstack chunk(1,2)-[1,2,3,4,5,6,7,8] - imgtx2 = np.hstack(imgt[:(nsecx-1):-1, :, :]) #hstack chunk(4,3)-[16,15,14,13,12,11,,10,9] + imgtx1 = np.hstack(imgt[:nsecx:, :, :]) # hstack chunk(1,2)-[1,2,3,4,5,6,7,8] + imgtx2 = np.hstack(imgt[:(nsecx-1):-1, :, :]) # hstack chunk(4,3)-[16,15,14,13,12,11,,10,9] newimg = galsim.Image(int(nx+(pre1+over1)*nsecx), int(ny+(pre2+over2)*nsecy), init_value=0) - newimg.array[:, :] = np.concatenate([imgtx1, imgtx2]) #vstack chunk(1,2) & chunk(4,3) + newimg.array[:, :] = np.concatenate([imgtx1, imgtx2]) # vstack chunk(1,2) & chunk(4,3) newimg.wcs = GSImage.wcs return newimg -### test FUNCTION --- END ### +# test FUNCTION --- END # def defineCCD(iccd, config_file): with open(config_file, "r") as stream: try: config = yaml.safe_load(stream) - #for key, value in config.items(): - # print (key + " : " + str(value)) + # for key, value in config.items(): + # print (key + " : " + str(value)) except yaml.YAMLError as exc: print(exc) chip = Chip(chipID=iccd, config=config) chip.img = galsim.ImageF(chip.npix_x, chip.npix_y) focal_plane = FocalPlane(chip_list=[iccd]) - chip.img.wcs= focal_plane.getTanWCS(192.8595, 27.1283, -113.4333*galsim.degrees, chip.pix_scale) + chip.img.wcs = focal_plane.getTanWCS(192.8595, 27.1283, -113.4333*galsim.degrees, chip.pix_scale) return chip + def defineFilt(chip): filter_param = FilterParam() filter_id, filter_type = chip.getChipFilter() @@ -91,13 +95,13 @@ class detModule_coverage(unittest.TestCase): print(chip.cen_pix_x, chip.cen_pix_y) chip.img = AddPreScan(GSImage=chip.img, - pre1=chip.prescan_x, - pre2=chip.prescan_y, - over1=chip.overscan_x, - over2=chip.overscan_y) + pre1=chip.prescan_x, + pre2=chip.prescan_y, + over1=chip.overscan_x, + over2=chip.overscan_y) - self.assertTrue( (chip.prescan_x+chip.overscan_x)*8+chip.npix_x == np.shape(chip.img.array)[1] ) - self.assertTrue( (chip.prescan_y+chip.overscan_y)*2+chip.npix_y == np.shape(chip.img.array)[0] ) + self.assertTrue((chip.prescan_x+chip.overscan_x)*8+chip.npix_x == np.shape(chip.img.array)[1]) + self.assertTrue((chip.prescan_y+chip.overscan_y)*2+chip.npix_y == np.shape(chip.img.array)[0]) if __name__ == '__main__': diff --git a/tools/get_PSF.py b/tools/get_PSF.py index f861bd5070d7f402e63a89b408d191224f3cf4e5..de711c9267ad7e2e509aab15b54f52e8fd43c73c 100644 --- a/tools/get_PSF.py +++ b/tools/get_PSF.py @@ -79,7 +79,7 @@ for iobj in range(nobj): if True: fn = "psf_{:}.{:}.{:}.fits".format(chipID, iobj, i) - if fn != None: + if fn is not None: if os.path.exists(fn): os.remove(fn) hdu = fitsio.PrimaryHDU() diff --git a/tools/get_pointing.py b/tools/get_pointing.py index 39216a16c7c2ed7c9cb7481a799c7211288e07aa..057fac67caa5920b134654f8b4626cd92e0d3869 100755 --- a/tools/get_pointing.py +++ b/tools/get_pointing.py @@ -2,7 +2,7 @@ # get_pointing # PURPOSE: # Return the pointing of CSST from a given [ra, dec] -# CALLING: +# CALLING: # pointing = findPointingbyChipID(chipID=tchip, ra=tra, dec=tdec) # INPUTS: # chipID - chip-# of CSST, int @@ -13,8 +13,8 @@ # HISTORY: # Written by Xin Zhang, 23 Apr. 2023 # Included by csst-simulation, C.W. 25 Apr. 2023 -# -# +# +# from tkinter.tix import INTEGER import astropy.coordinates as coord @@ -25,6 +25,7 @@ import galsim import math # from numba import jit + class Chip(object): def __init__(self, chipID): self.chipID = chipID @@ -41,9 +42,9 @@ class Chip(object): self.npix_x = 9216 self.npix_y = 9232 - self.pix_scale = 0.074 + self.pix_scale = 0.074 - def getTanWCS(self, ra, dec, img_rot, pix_scale = None, xcen=None, ycen=None, logger=None): + def getTanWCS(self, ra, dec, img_rot, pix_scale=None, xcen=None, ycen=None, logger=None): """ Get the WCS of the image mosaic using Gnomonic/TAN projection Parameter: @@ -57,11 +58,12 @@ class Chip(object): 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): + logger.info( + " Construct the wcs of the entire image mosaic using Gnomonic/TAN projection") + if (xcen is None) or (ycen is None): xcen = self.cen_pix_x ycen = self.cen_pix_y - if pix_scale == None: + if pix_scale is None: pix_scale = self.pix_scale # dudx = -np.cos(img_rot.rad) * pix_scale # dudy = -np.sin(img_rot.rad) * pix_scale @@ -72,23 +74,24 @@ class Chip(object): dudy = +np.sin(img_rot.rad) * pix_scale dvdx = -np.sin(img_rot.rad) * pix_scale dvdy = -np.cos(img_rot.rad) * pix_scale - + # dudx = +np.sin(img_rot.rad) * pix_scale # dudy = +np.cos(img_rot.rad) * pix_scale # dvdx = -np.cos(img_rot.rad) * pix_scale # dvdy = +np.sin(img_rot.rad) * pix_scale moscen = galsim.PositionD(x=xcen, y=ycen) - sky_center = galsim.CelestialCoord(ra=ra*galsim.degrees, dec=dec*galsim.degrees) + 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 getChipRowCol(self, chipID): rowID = ((chipID - 1) % 5) + 1 colID = 6 - ((chipID - 1) // 5) return rowID, colID - + def getChipCenter(self): """Calculate the edges in pixel for a given CCD chip on the focal plane NOTE: There are 5*4 CCD chips in the focus plane for photometric observation. @@ -99,8 +102,6 @@ class Chip(object): A galsim BoundsD object """ - - chipID = self.chipID rowID, colID = self.getChipRowCol(chipID) @@ -125,37 +126,42 @@ class Chip(object): return galsim.PositionD(xcen, ycen) + def transRaDec2D(ra, dec): - x1 = np.cos(dec / 57.2957795) * np.cos(ra / 57.2957795); - y1 = np.cos(dec / 57.2957795) * np.sin(ra / 57.2957795); - z1 = np.sin(dec / 57.2957795); + x1 = np.cos(dec / 57.2957795) * np.cos(ra / 57.2957795) + y1 = np.cos(dec / 57.2957795) * np.sin(ra / 57.2957795) + z1 = np.sin(dec / 57.2957795) return np.array([x1, y1, z1]) + def getobsPA(ra, dec): - l1 = np.array([0,0,1]) + l1 = np.array([0, 0, 1]) l2 = transRaDec2D(ra, dec) - polar_ec = coord.SkyCoord(0*u.degree, 90*u.degree,frame='barycentrictrueecliptic') + polar_ec = coord.SkyCoord(0*u.degree, 90*u.degree, + frame='barycentrictrueecliptic') polar_eq = polar_ec.transform_to('icrs') # print(polar_eq.ra.value,polar_eq.dec.value) polar_d = transRaDec2D(polar_eq.ra.value, polar_eq.dec.value) - l1l2cross = np.cross(l2,l1) - pdl2cross = np.cross(l2,polar_d) - angle = math.acos(np.dot(l1l2cross,pdl2cross)/(np.linalg.norm(l1l2cross)*np.linalg.norm(pdl2cross))) + l1l2cross = np.cross(l2, l1) + pdl2cross = np.cross(l2, polar_d) + angle = math.acos(np.dot(l1l2cross, pdl2cross) / + (np.linalg.norm(l1l2cross)*np.linalg.norm(pdl2cross))) angle = (angle)/math.pi*180 angle = angle + 90 - if (ra<90 or ra> 270): - angle=-angle + if (ra < 90 or ra > 270): + angle = -angle return angle # @jit() -def getSelectPointingList(center = [60,-40], radius = 2): + + +def getSelectPointingList(center=[60, -40], radius=2): points = np.loadtxt('sky.dat') - - center = center#ra dec - radius = radius # degree + center = center # ra dec + radius = radius # degree radii_dec = 1 radii_ra = 1/math.cos(math.pi*center[1]/180) @@ -163,7 +169,8 @@ def getSelectPointingList(center = [60,-40], radius = 2): if radii_ra > 180: radii_ra = 180 - c_eclip = coord.SkyCoord(points[:,2]*u.degree, points[:,1]*u.degree,frame='barycentrictrueecliptic') + c_eclip = coord.SkyCoord( + points[:, 2]*u.degree, points[:, 1]*u.degree, frame='barycentrictrueecliptic') c_equtor = c_eclip.transform_to('icrs') # print(np.min((c_equtor.ra*u.degree).value), np.max((c_equtor.ra*u.degree).value)) @@ -174,13 +181,15 @@ def getSelectPointingList(center = [60,-40], radius = 2): ra_range_lo = center[0]-radii_ra ra_range_hi = center[0]+radii_ra - if ra_range_lo< 0: - ids1 = ((c_equtor.ra*u.degree).value360+ra_range_lo) + if ra_range_lo < 0: + ids1 = ((c_equtor.ra*u.degree).value < + ra_range_hi) | ((c_equtor.ra*u.degree).value > 360+ra_range_lo) elif ra_range_hi > 360: - ids1 = ((c_equtor.ra*u.degree).value>ra_range_lo) | ((c_equtor.ra*u.degree).value + ra_range_lo) | ((c_equtor.ra*u.degree).value < ra_range_hi-360) else: - ids1 = ((c_equtor.ra*u.degree).value > ra_range_lo) & ((c_equtor.ra*u.degree).value < ra_range_hi) - + ids1 = ((c_equtor.ra*u.degree).value > + ra_range_lo) & ((c_equtor.ra*u.degree).value < ra_range_hi) dec_range_lo = center[1]-radii_dec if center[1]-radii_dec < -90: @@ -189,7 +198,7 @@ def getSelectPointingList(center = [60,-40], radius = 2): dec_range_hi = center[1]+radii_dec if center[1]+radii_dec > 90: dec_range_hi = 90 - + ids3 = (c_equtor[ids1].dec*u.degree).value > dec_range_lo ids4 = (c_equtor[ids1][ids3].dec*u.degree).value < dec_range_hi @@ -198,25 +207,24 @@ def getSelectPointingList(center = [60,-40], radius = 2): p_result = np.zeros([num, 5]) i = 0 - for p,p_ in zip(points[ids1][ids3][ids4],c_equtor[ids1][ids3][ids4]): + for p, p_ in zip(points[ids1][ids3][ids4], c_equtor[ids1][ids3][ids4]): ra = (p_.ra*u.degree).value dec = (p_.dec*u.degree).value # print(ra, dec) lon = p[2] lat = p[1] - p_result[i,0] = ra - p_result[i,1] = dec - p_result[i,2] = lon - p_result[i,3] = lat - p_result[i,4] = getobsPA(ra, dec) + 90 + p_result[i, 0] = ra + p_result[i, 1] = dec + p_result[i, 2] = lon + p_result[i, 3] = lat + p_result[i, 4] = getobsPA(ra, dec) + 90 i = i + 1 - - return p_result + return p_result -def findPointingbyChipID(chipID = 8, ra = 60., dec = -40.): +def findPointingbyChipID(chipID=8, ra=60., dec=-40.): """_summary_ Args: @@ -226,9 +234,9 @@ def findPointingbyChipID(chipID = 8, ra = 60., dec = -40.): Returns: _type_: [ra, dec, rotation angle] - """ + """ chip_center = [ra, dec] - p_list = getSelectPointingList(center = chip_center) + p_list = getSelectPointingList(center=chip_center) pchip = Chip(chipID) p_num = p_list.shape[0] @@ -238,10 +246,10 @@ def findPointingbyChipID(chipID = 8, ra = 60., dec = -40.): r_ra = ra r_dec = dec r_rot = 0. - for i in np.arange(0,p_num,1): - ra_n = p_list[i,0] - dec_n = p_list[i,1] - rot = p_list[i,4]*galsim.degrees + for i in np.arange(0, p_num, 1): + ra_n = p_list[i, 0] + dec_n = p_list[i, 1] + rot = p_list[i, 4]*galsim.degrees chip_wcs = pchip.getTanWCS(ra_n, dec_n, rot) c_center = pchip.getChipCenter() @@ -257,15 +265,14 @@ def findPointingbyChipID(chipID = 8, ra = 60., dec = -40.): r_ra = ra_n r_dec = dec_n r_rot = rot.deg - + if min_d == max_value: - print("RA:%f,Dec:%fä¸åœ¨æŒ‡å‘范围内,请于巡天规划åºåˆ—比对ï¼ï¼ï¼ï¼ï¼"%(ra, dec)) + print("RA:%f,Dec:%fä¸åœ¨æŒ‡å‘范围内,请于巡天规划åºåˆ—比对ï¼ï¼ï¼ï¼ï¼" % (ra, dec)) - return [r_ra, r_dec , r_rot] + return [r_ra, r_dec, r_rot] if __name__ == "__main__": tchip, tra, tdec = 13, 60., -40. pointing = findPointingbyChipID(chipID=tchip, ra=tra, dec=tdec) print("[ra_center, dec_center, image_rot]: ", pointing) - diff --git a/tools/get_pointing_accuracy.py b/tools/get_pointing_accuracy.py index 33d8575298c806b4631fe348b54c9bc690002260..7c531348da0c81aea91c946b76ac12b726511f8c 100644 --- a/tools/get_pointing_accuracy.py +++ b/tools/get_pointing_accuracy.py @@ -1,6 +1,8 @@ from pylab import * -import math, sys, numpy as np +import math +import sys +import numpy as np import astropy.coordinates as coord from astropy.coordinates import SkyCoord from astropy import wcs, units as u @@ -14,8 +16,9 @@ def transRaDec2D(ra, dec): z1 = np.sin(dec / 57.2957795) return np.array([x1, y1, z1]) + def ecl2radec(lon_ecl, lat_ecl): - ## convert from ecliptic coordinates to equatorial coordinates + # convert from ecliptic coordinates to equatorial coordinates c_ecl = SkyCoord( lon=lon_ecl * u.degree, lat=lat_ecl * u.degree, frame="barycentrictrueecliptic" ) @@ -25,25 +28,26 @@ def ecl2radec(lon_ecl, lat_ecl): def radec2ecl(ra, dec): - ## convert from equatorial coordinates to ecliptic coordinates + # convert from equatorial coordinates to ecliptic coordinates c_eq = SkyCoord(ra=ra * u.degree, dec=dec * u.degree, frame="icrs") c_ecl = c_eq.transform_to("barycentrictrueecliptic") lon_ecl, lat_ecl = c_ecl.lon.degree, c_ecl.lat.degree return lon_ecl, lat_ecl -def cal_FoVcenter_1P_equatorial(ra_FieldCenter, dec_FieldCenter, chipID = 1, pa = -23.5): - ### [ra_FieldCenter, dec_FieldCenter] is the center ra, dec of calibration fileds, such as: NEP, NGC 6397, etc. - ### [ra_ChipCenter, dec_ChipCenter] is the center ra, dec of the Chip center. - ### [ra_PointCenter, dec_PointCenter] is the telescope pointing center. - ## Calculate PA angle +def cal_FoVcenter_1P_equatorial(ra_FieldCenter, dec_FieldCenter, chipID=1, pa=-23.5): + + # [ra_FieldCenter, dec_FieldCenter] is the center ra, dec of calibration fileds, such as: NEP, NGC 6397, etc. + # [ra_ChipCenter, dec_ChipCenter] is the center ra, dec of the Chip center. + # [ra_PointCenter, dec_PointCenter] is the telescope pointing center. + # Calculate PA angle chip = Chip(chipID) h_ext = ImageHeader.generateExtensionHeader( chip=chip, xlen=chip.npix_x, ylen=chip.npix_y, - ra=(ra_FieldCenter * u.degree).value, + ra=(ra_FieldCenter * u.degree).value, dec=(dec_FieldCenter * u.degree).value, pa=pa, gain=chip.gain, @@ -54,7 +58,7 @@ def cal_FoVcenter_1P_equatorial(ra_FieldCenter, dec_FieldCenter, chipID = 1, pa pixel_size=chip.pix_size, xcen=chip.x_cen, ycen=chip.y_cen, - ) + ) # assume that the telescope point to the sky center; then abtain the chip center under this situation; then calculate the differenc between the assumed telescope center and chip center; then add this difference to the sky center @@ -75,24 +79,25 @@ def cal_FoVcenter_1P_equatorial(ra_FieldCenter, dec_FieldCenter, chipID = 1, pa return ra_PointCenter, dec_PointCenter, lon_ecl_PointCenter, lat_ecl_PointCenter -def cal_FoVcenter_1P_ecliptic(lon_ecl_FieldCenter, lat_ecl_FieldCenter, chipID = 1, pa = -23.5): - ### [ra_FieldCenter, dec_FieldCenter] is the center ra, dec of calibration fileds, such as: NEP, NGC 6397, etc. - ### [ra_ChipCenter, dec_ChipCenter] is the center ra, dec of the Chip center. - ### [ra_PointCenter, dec_PointCenter] is the telescope pointing center. +def cal_FoVcenter_1P_ecliptic(lon_ecl_FieldCenter, lat_ecl_FieldCenter, chipID=1, pa=-23.5): + + # [ra_FieldCenter, dec_FieldCenter] is the center ra, dec of calibration fileds, such as: NEP, NGC 6397, etc. + # [ra_ChipCenter, dec_ChipCenter] is the center ra, dec of the Chip center. + # [ra_PointCenter, dec_PointCenter] is the telescope pointing center. ra_FieldCenter, dec_FieldCenter = ecl2radec( lon_ecl_FieldCenter, lat_ecl_FieldCenter ) - ## Calculate PA angle + # Calculate PA angle chip = Chip(chipID) h_ext = ImageHeader.generateExtensionHeader( chip=chip, xlen=chip.npix_x, ylen=chip.npix_y, - ra=(ra_FieldCenter * u.degree).value, + ra=(ra_FieldCenter * u.degree).value, dec=(dec_FieldCenter * u.degree).value, pa=pa, gain=chip.gain, @@ -103,7 +108,7 @@ def cal_FoVcenter_1P_ecliptic(lon_ecl_FieldCenter, lat_ecl_FieldCenter, chipID = pixel_size=chip.pix_size, xcen=chip.x_cen, ycen=chip.y_cen, - ) + ) # assume that the telescope point to the sky center; then abtain the chip center under this situation; then calculate the differenc between the assumed telescope center and chip center; then add this difference to the sky center @@ -124,14 +129,15 @@ def cal_FoVcenter_1P_ecliptic(lon_ecl_FieldCenter, lat_ecl_FieldCenter, chipID = return ra_PointCenter, dec_PointCenter, lon_ecl_PointCenter, lat_ecl_PointCenter -def getChipCenterRaDec(chipID = 1, p_ra = 60., p_dec = -40.): + +def getChipCenterRaDec(chipID=1, p_ra=60., p_dec=-40.): chip = Chip(chipID) h_ext = ImageHeader.generateExtensionHeader( chip=chip, xlen=chip.npix_x, ylen=chip.npix_y, - ra=(p_ra * u.degree).value, + ra=(p_ra * u.degree).value, dec=(p_dec * u.degree).value, pa=pa, gain=chip.gain, @@ -142,24 +148,25 @@ def getChipCenterRaDec(chipID = 1, p_ra = 60., p_dec = -40.): pixel_size=chip.pix_size, xcen=chip.x_cen, ycen=chip.y_cen, - ) + ) wcs_h = wcs.WCS(h_ext) pixs = np.array([[4608, 4616]]) world_point = wcs_h.wcs_pix2world(pixs, 0) RA_chip, Dec_chip = world_point[0][0], world_point[0][1] return RA_chip, Dec_chip + if __name__ == '__main__': ra_input, dec_input = 270.00000, 66.56000 # NEP pa = 23.5 # chipid = 2 - for chipid in np.arange(1,31,1): + for chipid in np.arange(1, 31, 1): ra, dec, lon_ecl, lat_ecl = cal_FoVcenter_1P_equatorial( - ra_input, dec_input,chipID=chipid, pa=pa) + ra_input, dec_input, chipID=chipid, pa=pa) - print("chip id is %d, chip center [ra,dec] is [%f, %f], pointing center calculated [ra,dec] is [%f, %f]"%(chipid, ra_input, dec_input, ra, dec)) - #for check the result + print("chip id is %d, chip center [ra,dec] is [%f, %f], pointing center calculated [ra,dec] is [%f, %f]" % ( + chipid, ra_input, dec_input, ra, dec)) + # for check the result # testRA, testDec = getChipCenterRaDec(chipID = chipid, p_ra = ra, p_dec = dec) # print(ra_input-testRA, dec_input-testDec) - diff --git a/tools/setConfig/reset_obs.py b/tools/setConfig/reset_obs.py new file mode 100644 index 0000000000000000000000000000000000000000..f05b9d2448458b35e6eb5c280d6eece1384d7991 --- /dev/null +++ b/tools/setConfig/reset_obs.py @@ -0,0 +1,122 @@ +from flask import Flask, render_template, request, redirect +import yaml +import ast + +app = Flask(__name__) + + +key_type_map = { + 'obs_type': str, + 'obs_type_code': str, + 'obs_id': str, + 'run_chips': list, + 'call_sequence': { + 'scie_obs': { + 'shutter_effect': bool, + 'flat_fielding': bool, + 'field_dist': bool, + }, + 'sky_background': { + 'shutter_effect': bool, + 'flat_fielding': bool, + 'enable_straylight_model': bool, + 'flat_level': None, + 'flat_level_filt': None, + }, + 'PRNU_effect': {}, + 'cosmic_rays': { + 'save_cosmic_img': bool, + }, + 'poisson_and_dark': { + 'add_dark': bool, + }, + 'bright_fatter': {}, + 'detector_defects': { + 'hot_pixels': bool, + 'dead_pixels': bool, + 'bad_columns': bool, + }, + 'nonlinearity': {}, + 'blooming': {}, + 'prescan_overscan': { + 'add_dark': bool, + }, + 'bias': { + 'bias_16channel': bool, + }, + 'readout_noise': {}, + 'gain': { + 'gain_16channel': bool, + }, + 'quantization_and_output': { + 'format_output': bool, + }, + }, +} + + +def convert_dict_values(d, key_type_map): + for key, value in d.items(): + if isinstance(value, dict): + convert_dict_values(value, key_type_map[key]) + elif key in key_type_map: + if key_type_map[key] is int: + d[key] = int(value) + if key_type_map[key] is float: + d[key] = float(value) + if key_type_map[key] is bool: + if d[key].lower() == 'yes' or d[key].lower() == 'true': + d[key] = True + else: + d[key] = False + if key_type_map[key] is str: + d[key] = str(value) + if key_type_map[key] is list: + d[key] = ast.literal_eval(value) + if key_type_map[key] is None: + d[key] = None + + +def load_yaml(): + with open('templates/obs_config_SCI.yaml', 'r') as file: + return yaml.safe_load(file) + + +def save_yaml(data): + convert_dict_values(data, key_type_map) + with open('config_reset/obs_config_SCI_reset.yaml', 'w') as file: + yaml.dump(data, file, default_flow_style=False, sort_keys=False) + + +def render_form(data, parent_key=''): + form_html = '' + for key, value in data.items(): + full_key = f"{parent_key}.{key}" if parent_key else key + if isinstance(value, dict): # 处ç†å­—å…¸ + form_html += f"

{key}

{render_form(value, full_key)}
" + else: + form_html += f"" + form_html += f"
" + return form_html + + +@app.route('/', methods=['GET', 'POST']) +def index(): + if request.method == 'POST': + data = load_yaml() + for key in request.form: + keys = key.split('.') + temp = data + for k in keys[:-1]: + temp = temp[k] + temp[keys[-1]] = request.form[key] + save_yaml(data) + return redirect('/') + + data = load_yaml() + form_html = render_form(data) + return render_template('index_obs.html', form_html=form_html) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/tools/setConfig/reset_overall.py b/tools/setConfig/reset_overall.py new file mode 100644 index 0000000000000000000000000000000000000000..cacb2049210ed85717c103baa26fc2dc01be32d2 --- /dev/null +++ b/tools/setConfig/reset_overall.py @@ -0,0 +1,133 @@ +from flask import Flask, render_template, request, redirect +import yaml +import ast + +app = Flask(__name__) + +key_type_map = { + 'work_dir': str, + 'run_name': str, + 'project_cycle': int, + 'run_counter': int, + 'run_option': { + 'out_cat_only': bool, + }, + 'catalog_options': { + 'input_path': { + 'cat_dir': str, + 'star_cat': str, + 'galaxy_cat': str, + }, + 'SED_templates_path': { + 'star_SED': str, + 'galaxy_SED': str, + 'AGN_SED': str, + }, + 'star_only': bool, + 'galaxy_only': bool, + 'rotateEll': float, + 'enable_mw_ext_gal': bool, + 'planck_ebv_map': str, + }, + 'obs_setting': { + 'pointing_file': str, + 'obs_config_file': str, + 'run_pointings': list, + 'enable_astrometric_model': bool, + 'cut_in_band': str, + 'mag_sat_margin': float, + 'mag_lim_margin': float, + }, + 'psf_setting': { + 'psf_model': str, + 'psf_pho_dir': str, + 'psf_sls_dir': str, + }, + 'shear_setting': { + 'shear_type': str, + 'reduced_g1': float, + 'reduced_g2': float, + }, + 'output_setting': { + 'output_format': str, + 'shutter_output': bool, + 'prnu_output': bool, + }, + 'random_seeds': { + 'seed_poisson': int, + 'seed_CR': int, + 'seed_flat': int, + 'seed_prnu': int, + 'seed_gainNonUniform': int, + 'seed_biasNonUniform': int, + 'seed_rnNonUniform': int, + 'seed_badcolumns': int, + 'seed_defective': int, + 'seed_readout': int, + }, +} + + +def convert_dict_values(d, key_type_map): + for key, value in d.items(): + if isinstance(value, dict): + convert_dict_values(value, key_type_map[key]) + elif key in key_type_map: + if key_type_map[key] is int: + d[key] = int(value) + if key_type_map[key] is float: + d[key] = float(value) + if key_type_map[key] is bool: + if d[key].lower() == 'yes' or d[key].lower() == 'true': + d[key] = True + else: + d[key] = False + if key_type_map[key] is str: + d[key] = str(value) + if key_type_map[key] is list: + d[key] = ast.literal_eval(value) + + +def load_yaml(): + with open('templates/config_overall.yaml', 'r') as file: + return yaml.safe_load(file) + + +def save_yaml(data): + convert_dict_values(data, key_type_map) + with open('config_reset/config_overall_reset.yaml', 'w') as file: + yaml.dump(data, file, default_flow_style=False, sort_keys=False) + + +def render_form(data, parent_key=''): + form_html = '' + for key, value in data.items(): + full_key = f"{parent_key}.{key}" if parent_key else key + if isinstance(value, dict): # 处ç†å­—å…¸ + form_html += f"

{key}

{render_form(value, full_key)}
" + else: + form_html += f"" + form_html += f"
" + return form_html + + +@app.route('/', methods=['GET', 'POST']) +def index(): + if request.method == 'POST': + data = load_yaml() + for key in request.form: + keys = key.split('.') + temp = data + for k in keys[:-1]: + temp = temp[k] + temp[keys[-1]] = request.form[key] + save_yaml(data) + return redirect('/') + + data = load_yaml() + form_html = render_form(data) + return render_template('index_overall.html', form_html=form_html) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/tools/setConfig/templates/config_overall.yaml b/tools/setConfig/templates/config_overall.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d22dc6847dd3ee0ade13373233a91e38ec1f5770 --- /dev/null +++ b/tools/setConfig/templates/config_overall.yaml @@ -0,0 +1,145 @@ +--- +############################################### +# +# Configuration file for CSST simulation +# Overall settings +# CSST-Sim Group, 2024/01/08 +# +############################################### + +# Base diretories and naming setup +# can add some of the command-line arguments here as well; +# ok to pass either way or both, as long as they are consistent +work_dir: "/public/home/fangyuedong/project/workplace/" +run_name: "ext_on" + +# Project cycle and run counter are used to name the outputs +project_cycle: 9 +run_counter: 1 + +# Run options +run_option: + # Output catalog only? + # If yes, no imaging simulation will be run. Only the catalogs + # of corresponding footprints will be generated. + out_cat_only: NO + +############################################### +# Catalog setting +############################################### +# Configure the input catalog: options should be implemented +# in the corresponding (user defined) 'Catalog' class +catalog_options: + input_path: + cat_dir: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/" + star_cat: "starcat_C9/" + galaxy_cat: "qsocat/cat2CSSTSim_bundle-50sqDeg/" + + SED_templates_path: + star_SED: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/starcat_C9/" + galaxy_SED: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/sedlibs/" + AGN_SED: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/qsocat/qsosed/" + + # Only simulate stars? + star_only: NO + + # Only simulate galaxies? + galaxy_only: NO + + # rotate galaxy ellipticity + rotateEll: 0. # [degree] + + # Whether to apply milky way extinction to galaxies + enable_mw_ext_gal: YES + planck_ebv_map: "/public/home/fangyuedong/project/ext_maps/planck/HFI_CompMap_ThermalDustModel_2048_R1.20.fits" + +############################################### +# Observation setting +############################################### +obs_setting: + # (Optional) a file of point list + # if you just want to run default pointing: + # - pointing_dir: null + # - pointing_file: null + pointing_file: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/pointing50_C9/pointing_50_1_n.dat" + + obs_config_file: "/public/home/fangyuedong/project/csst_msc_sim/config/obs_config_SCI.yaml" + + # Run specific pointing(s): + # - give a list of indexes of pointings: [ip_1, ip_2...] + # - run all pointings: null + # Note: only valid when a pointing list is specified + run_pointings: [0, 1, 2, 3, 4] + + # Whether to enable astrometric modeling + enable_astrometric_model: YES + + # Cut by saturation magnitude in which band? + cut_in_band: "z" + + # saturation magnitude margin + mag_sat_margin: -2.5 + # mag_sat_margin: -15. + + # limiting magnitude margin + mag_lim_margin: +1.0 + +############################################### +# PSF setting +############################################### +psf_setting: + + # Which PSF model to use: + # "Gauss": simple gaussian profile + # "Interp": Interpolated PSF from sampled ray-tracing data + psf_model: "Interp" + + # PSF size [arcseconds] + # radius of 80% energy encircled + # NOTE: only valid for "Gauss" PSF + # psf_rcont: 0.15 + + # path to PSF data + # NOTE: only valid for "Interp" PSF + # PSF models for photometry survey simulation + psf_pho_dir: "/public/share/yangxuliu/CSSOSDataProductsSims/dataC6/psfCube1" + # PSF models for slitless spectrum survey simulation + psf_sls_dir: "/public/share/yangxuliu/CSSOSDataProductsSims/data_50sqDeg/SLS_PSF_PCA_fp/" + +############################################### +# Shear setting +############################################### + +shear_setting: + # Options to generate mock shear field: + # "constant": all galaxies are assigned a constant reduced shear + # "catalog": get shear values from catalog + shear_type: "constant" + + # For constant shear field + reduced_g1: 0. + reduced_g2: 0. + +############################################### +# Output options +############################################### +output_setting: + output_format: "channels" # Whether to export as 16 channels (subimages) with pre- and over-scan ("image"/"channels") + shutter_output: NO # Whether to export shutter effect 16-bit image + prnu_output: NO # Whether to export the PRNU (pixel-to-pixel flat-fielding) files + +############################################### +# Random seeds +############################################### +random_seeds: + seed_poisson: 20210601 # Seed for Poisson noise + seed_CR: 20210317 # Seed for generating random cosmic ray maps + seed_flat: 20210101 # Seed for generating random flat fields + seed_prnu: 20210102 # Seed for photo-response non-uniformity + seed_gainNonUniform: 20210202 # Seed for gain nonuniformity + seed_biasNonUniform: 20210203 # Seed for bias nonuniformity + seed_rnNonUniform: 20210204 # Seed for readout-noise nonuniformity + seed_badcolumns: 20240309 # Seed for bad columns + seed_defective: 20210304 # Seed for defective (bad) pixels + seed_readout: 20210601 # Seed for read-out gaussian noise +... \ No newline at end of file diff --git a/tools/setConfig/templates/index_obs.html b/tools/setConfig/templates/index_obs.html new file mode 100644 index 0000000000000000000000000000000000000000..5199558d997fceadbd7caa31daa7f39b19508254 --- /dev/null +++ b/tools/setConfig/templates/index_obs.html @@ -0,0 +1,246 @@ + + + + + + csst_msc_sim_CONF + + + + +
+

é…ç½®obs_config_SCI.yaml

+

说明: + 1,该脚本å¯ç”¨äºŽç”ŸæˆCSST主巡天æˆåƒä»¿çœŸçš„傿•°æ–‡ä»¶,ç›¸å…³å‚æ•°è¯´æ˜Žè§æœ¬é¡µè„𿳍;2,ç”¨æˆ·å¿…é¡»ä¿®æ”¹ç›¸å…³è·¯å¾„å‚æ•°ï¼Œå…¶ä»–傿•°å¯å‚考默认值 +. +

+
+
+ {{ form_html | safe }} + +
+
+ +
+

这是一个有用的网页

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KEYSVALUESCOMMENTS
obs_type"SCI", [str]仿真图åƒç±»åž‹
obs_type_code"101", [str]æ•°æ®ç³»ç»Ÿå†…部标å·
obs_id"00000001", [str]this setting will only be used if pointing list file is not given
run_chips[7,8,9], [list]Define list of chips
scie_obs:shutter_effectYES, [bool]是å¦åº”用快门效应
scie_obs:flat_fieldingYES, [bool]是å¦åº”用平场模å—
scie_obs:field_distYES, [bool]
sky_background:shutter_effectYES, [bool]是å¦åº”用快门效应
sky_background:flat_fieldingYES, [bool]是å¦åº”用平场模å—
sky_background:enable_straylight_modelYES, [bool]是å¦åº”ç”¨æ‚æ•£å…‰æ¨¡å—
sky_background:flat_levelnull [null or int]set the total skybackground value (e-) in the exptime,if none,set null, or delete the key
sky_background:flat_level_filtnull, [str]the vale of "flat_level" is in the filter "flat_level_filt", can set NUV, u, g, r, i, z, y, if none,set null,or delete the key
PRNU_effect---应用åƒç´ é—´ä¸å‡åŒ€å“应模å—;如果需关闭,请在生æˆçš„yaml文件中直接注释
cosmic_rays:save_cosmic_imgYES, [bool]是å¦ä¿å­˜å®‡å®™çº¿å›¾åƒ
poisson_and_dark:add_darkYES, [bool]æ˜¯å¦æ·»åŠ æš—ç”µæµ
bright_fatterYES, [bool]应用亮胖效应模å—;如果需关闭,请在生æˆçš„yaml文件中直接注释
detector_defects:hot_pixelsYES, [bool]æ˜¯å¦æ·»åŠ çƒ­åƒå…ƒ
detector_defects:dead_pixelsYES, [bool]æ˜¯å¦æ·»åŠ ååƒå…ƒ
detector_defects:bad_columnsYES, [bool]æ˜¯å¦æ·»åŠ ååƒåˆ—
nonlinearity---应用éžçº¿æ€§å“应模å—;如果需关闭,请在生æˆçš„yaml文件中直接注释
blooming---是å¦åº”用饱和溢出模å—;如果需关闭,请在生æˆçš„yaml文件中直接注释
prescan_overscan:add_darkYES, [bool] 是å¦åœ¨pre/over-scan区域添加暗电æµ/
bias:bias_16channelYES, [bool]æ˜¯å¦æ·»åŠ 16通é“çš„å置电压
readout_noise---添加读出噪声;如果需关闭,请在生æˆçš„yaml文件中直接注释
gain:gain_16channelYES, [bool]是å¦åº”用增益
quantization_and_output:format_outputYES, [bool]æ˜¯å¦æŒ‰0çº§æ•°æ®æ ¼å¼å®šä¹‰è¾“出图åƒ
+
+ + + diff --git a/tools/setConfig/templates/index_overall.html b/tools/setConfig/templates/index_overall.html new file mode 100644 index 0000000000000000000000000000000000000000..d61ccf82cd4be5e981c9b57a09eb19bc9d64605d --- /dev/null +++ b/tools/setConfig/templates/index_overall.html @@ -0,0 +1,258 @@ + + + + + + csst_msc_sim_CONF + + + + +
+

é…ç½®config_overall.yaml

+

说明: + 1,该脚本å¯ç”¨äºŽç”ŸæˆCSST主巡天æˆåƒä»¿çœŸçš„傿•°æ–‡ä»¶,ç›¸å…³å‚æ•°è¯´æ˜Žè§æœ¬é¡µè„𿳍;2,ç”¨æˆ·å¿…é¡»ä¿®æ”¹ç›¸å…³è·¯å¾„å‚æ•°ï¼Œå…¶ä»–傿•°å¯å‚考默认值. +

+
+
+ {{ form_html | safe }} + +
+
+ + + + + diff --git a/tools/setConfig/templates/obs_config_SCI.yaml b/tools/setConfig/templates/obs_config_SCI.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ef82bcdc6c0e17cc788ca396dd4fa3be087a159a --- /dev/null +++ b/tools/setConfig/templates/obs_config_SCI.yaml @@ -0,0 +1,85 @@ +--- +############################################### +# +# Configuration file for CSST simulation +# For single exposure type: +# SCI-WIDE +# CSST-Sim Group, 2024/01/08 +# +############################################### + +# Observation type +obs_type: "SCI" +obs_type_code: "101" +obs_id: "00000001" # this setting will only be used if pointing list file is not given + +# Define list of chips +# run_chips: [6,7,8,9,11,12,13,14,15,16,17,18,19,20,22,23,24,25] # Photometric chips +#run_chips: [1,2,3,4,5,10,21,26,27,28,29,30] # Spectroscopic chips +run_chips: [17, 22] + +# Define observation sequence +call_sequence: + # Accumulate fluxes from objects + scie_obs: + # [Optional]: exposure time of the pointing will be used as default. + # Set it here is you want to override the default + # exptime: 150. # [s] + shutter_effect: YES + flat_fielding: YES + field_dist: YES + # Accumulate fluxes from sky background + sky_background: + # [Optional]: exposure time of the pointing will be used as default. + # Set it here is you want to override the default + # exptime: 150. # [s] + shutter_effect: YES + flat_fielding: YES + enable_straylight_model: YES + # flat_level: set the total skybackground value (e-) in the exptime,if none,set null, or delete the key + # flat_level_filt: the vale of "flat_level" is in the filter "flat_level_filt", can set NUV, u, g, r, i, z, y, if + # none,set null,or delete the key + flat_level: null + flat_level_filt: null + # Apply PRNU to accumulated photons + PRNU_effect: {} + # Accumulate photons caused by cosmic rays + cosmic_rays: + # [Optional]: exposure time of the pointing will be used as default. + # Set it here is you want to override the default + # exptime: 150. # [s] + save_cosmic_img: YES # # Whether to export cosmic ray image + # Add Poission noise and dark current + poisson_and_dark: + # [Optional]: exposure time of the pointing will be used as default. + # Set it here is you want to override the default + # exptime: 150. # [s] + 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_dark: YES + # Add bias + bias: + bias_16channel: YES + # Add readout noise + readout_noise: {} + # Apply gain + gain: + gain_16channel: YES + # Output the final image + quantization_and_output: + format_output: YES +... diff --git a/tools/target_location_check.py b/tools/target_location_check.py index 11f22bac7e8eb8d74606c4e5d003ac92e5e23633..6e0f525ea3b4a35d95140f04cb2f7d470ff69eec 100644 --- a/tools/target_location_check.py +++ b/tools/target_location_check.py @@ -26,7 +26,7 @@ def focalPlaneInf(ra_target, dec_target, ra_point, dec_point, image_rot=-113.433 float, in unit of degrees; image_rot : orientation of the camera with respect the sky; float, in unit of degrees; - NOTE: image_rot=-113.4333 is the default value + NOTE: image_rot=-113.4333 is the default value in current CSST image simulation; figout : location of the target object in the focal plane; str @@ -36,7 +36,7 @@ def focalPlaneInf(ra_target, dec_target, ra_point, dec_point, image_rot=-113.433 the pointing center (ra_point dec_point) of the telescope 1) open a terminal 2) type >> python TargetLocationCheck.py ra_target dec_target ra_point dec_point - or type >> python TargetLocationCheck.py ra_target dec_target ra_point dec_point -image_rot=floatNum + or type >> python TargetLocationCheck.py ra_target dec_target ra_point dec_point -image_rot=floatNum or type >> python TargetLocationCheck.py ra_target dec_target ra_point dec_point -image_rot=floatNum -figout=FigureName """ print("^_^ Input target coordinate: [Ra, Dec] = [%10.6f, %10.6f]" % ( @@ -105,7 +105,7 @@ def ccdParam(): def getTanWCS(ra, dec, img_rot, pix_scale=0.074): - """ + """ Get the WCS of the image mosaic using Gnomonic/TAN projection Parameter: ra, dec: float @@ -221,8 +221,8 @@ def ccdLayout(xpixTar, ypixTar, figout="ccdLayout.pdf"): ax.text(ix0+500, iy0+1500, "%s#%s" % (fType, ischip), fontsize=12, color="grey") ax.plot(xpixTar, ypixTar, "r*", ms=12) - ax.set_xlabel("$X\,[\mathrm{pixels}]$", fontsize=20) - ax.set_ylabel("$Y\,[\mathrm{pixels}]$", fontsize=20) + ax.set_xlabel(r"$X\,[\mathrm{pixels}]$", fontsize=20) + ax.set_ylabel(r"$Y\,[\mathrm{pixels}]$", fontsize=20) ax.invert_yaxis() ax.axis('off') plt.savefig(figout)