Source code for RECTE

# ! /usr/bin/env pythonp
"""ramp effect model

Version 1.0.0: published version

Version 0.1: Adapted original IDL code (From D. Apai) to python by Yifan Zhou

"""
import numpy as np
import itertools


[docs]def RECTE( cRates, tExp, exptime=180, trap_pop_s=0, trap_pop_f=0, dTrap_s=0, dTrap_f=0, dt0=0, lost=0, mode='scanning' ): """This function calculates HST/WFC3/IR ramp effect profile based on the charge trapping explanation developed in Zhou et al. (2017). :param cRates: intrinsic count rate of each exposures, unit: e/s :type cRates: numpy.array :param tExp: time stamps for the exposures, unit: seconds :type tExp: numpy.array :param exptime: (default 180 seconds) exposure time :type exptime: numpy.array or float :param trap_pop_s: (default 0) number of occupied slow population charge traps before the very beginning of the observation :type trap_pop_s: float or numpy.array :param trap_pop_f: (default 0) number of occupied fast population charge traps before the very beginning of the observation :type trap_pop_f: float or numpy.array :param dTrap_s: (default [0]) number of additional charges trapped by slow population traps during earth occultation :type dTrap_s: float or numpy.array :param dTrap_f: (default [0]) number of additional charges trapped by fast population traps during earth occultation :type dTrap_f: float or numpy.array :param dt0: (default 0) exposure time before the very beginning of the observation. It could be due to guidence adjustment :type dt0: float :param lost: (default 0, no lost) fraction of trapped electrons that are not eventually detected :type lost: float :param mode: (default scanning, scanning or staring, or others), for scanning mode observation , the pixel no longer receive photons during the overhead time, in staring mode, the pixel keps receiving elctrons :type mode: string :returns: observed counts :rtype: numpy.array :Example: see Examples and Cookbook """ nTrap_s = 1525.38 eta_trap_s = 0.013318 tau_trap_s = 1.63e4 nTrap_f = 162.38 eta_trap_f = 0.008407 tau_trap_f = 281.463 try: dTrap_f = itertools.cycle(dTrap_f) dTrap_s = itertools.cycle(dTrap_s) dt0 = itertools.cycle(dt0) except TypeError: dTrap_f = itertools.cycle([dTrap_f]) dTrap_s = itertools.cycle([dTrap_s]) dt0 = itertools.cycle([dt0]) obsCounts = np.zeros(len(tExp)) trap_pop_s = min(trap_pop_s, nTrap_s) trap_pop_f = min(trap_pop_f, nTrap_f) for i in range(len(tExp)): try: dt = tExp[i+1] - tExp[i] except IndexError: dt = exptime f_i = cRates[i] c1_s = eta_trap_s * f_i / nTrap_s + 1 / tau_trap_s # a key factor c1_f = eta_trap_f * f_i / nTrap_f + 1 / tau_trap_f # number of trapped electron during one exposure dE1_s = (eta_trap_s * f_i / c1_s - trap_pop_s) * \ (1 - np.exp(-c1_s * exptime)) dE1_f = (eta_trap_f * f_i / c1_f - trap_pop_f) * \ (1 - np.exp(-c1_f * exptime)) dE1_s = min(trap_pop_s + dE1_s, nTrap_s) - trap_pop_s dE1_f = min(trap_pop_f + dE1_f, nTrap_f) - trap_pop_f trap_pop_s = min(trap_pop_s + dE1_s, nTrap_s) trap_pop_f = min(trap_pop_f + dE1_f, nTrap_f) obsCounts[i] = f_i * exptime - dE1_s - dE1_f if dt < 5 * exptime: # whether next exposure is in next batch of exposures # same orbits if mode == 'scanning': # scanning mode, no incoming flux between exposures dE2_s = - trap_pop_s * (1 - np.exp(-(dt - exptime)/tau_trap_s)) dE2_f = - trap_pop_f * (1 - np.exp(-(dt - exptime)/tau_trap_f)) elif mode == 'staring': # for staring mode, there is flux between exposures dE2_s = (eta_trap_s * f_i / c1_s - trap_pop_s) * \ (1 - np.exp(-c1_s * (dt - exptime))) dE2_f = (eta_trap_f * f_i / c1_f - trap_pop_f) * \ (1 - np.exp(-c1_f * (dt - exptime))) else: # others, same as scanning dE2_s = - trap_pop_s * (1 - np.exp(-(dt - exptime)/tau_trap_s)) dE2_f = - trap_pop_f * (1 - np.exp(-(dt - exptime)/tau_trap_f)) trap_pop_s = min(trap_pop_s + dE2_s, nTrap_s) trap_pop_f = min(trap_pop_f + dE2_f, nTrap_f) elif dt < 1200: # considering in orbit download scenario trap_pop_s = min( trap_pop_s * np.exp(-(dt-exptime)/tau_trap_s), nTrap_s) trap_pop_f = min( trap_pop_f * np.exp(-(dt-exptime)/tau_trap_f), nTrap_f) else: # switch orbit dt0_i = next(dt0) trap_pop_s = min(trap_pop_s * np.exp(-(dt-exptime-dt0_i)/tau_trap_s) + next(dTrap_s), nTrap_s) trap_pop_f = min(trap_pop_f * np.exp(-(dt-exptime-dt0_i)/tau_trap_f) + next(dTrap_f), nTrap_f) f_i = cRates[i + 1] c1_s = eta_trap_s * f_i / nTrap_s + 1 / tau_trap_s # a key factor c1_f = eta_trap_f * f_i / nTrap_f + 1 / tau_trap_f dE3_s = (eta_trap_s * f_i / c1_s - trap_pop_s) * \ (1 - np.exp(-c1_s * dt0_i)) dE3_f = (eta_trap_f * f_i / c1_f - trap_pop_f) * \ (1 - np.exp(-c1_f * dt0_i)) dE3_s = min(trap_pop_s + dE3_s, nTrap_s) - trap_pop_s dE3_f = min(trap_pop_f + dE3_f, nTrap_f) - trap_pop_f trap_pop_s = min(trap_pop_s + dE3_s, nTrap_s) trap_pop_f = min(trap_pop_f + dE3_f, nTrap_f) trap_pop_s = max(trap_pop_s, 0) trap_pop_f = max(trap_pop_f, 0) return obsCounts
if __name__ == '__main__': pass