Beispiel #1
0
def run(opt, channel, frame_time, total_observing_time, exposure_time):
    exosim_msg('Create noise timelines ... ')
    st = time.time()

    yaw_jitter = pitch_jitter = frame_osf = None

    jitter_file = opt.aocs.PointingModel().replace("__path__", opt.__path__)
    if hasattr(opt.aocs, 'pointing_rms'):
        jit_rms = opt.aocs.pointing_rms()
    else:
        jit_rms = None

    yaw_jitter, pitch_jitter, frame_osf = exolib.pointing_jitter(
        jitter_file, total_observing_time, frame_time, rms=jit_rms)

    if opt.aocs.pointing_scan_throw() > 0:
        pitch_jitter = exolib.pointing_add_scan(
            pitch_jitter,
            scan_throw_arcsec=opt.aocs.pointing_scan_throw(),
            frame_time=frame_time,
            frame_osf=frame_osf,
            exposure_time=exposure_time)

    for key in channel.keys():

        key, noise, outputPointingTl = channel_noise_estimator(
            channel[key], key, yaw_jitter, pitch_jitter, frame_osf, frame_time,
            opt, total_observing_time, exposure_time)
        channel[key].noise = noise
        channel[key].outputPointingTl = outputPointingTl

    exosim_msg(' - execution time: {:.0f} msec.\n'.format(
        (time.time() - st) * 1000.0))
Beispiel #2
0
def run(opt, channel, frame_time, total_observing_time, exposure_time):
  exosim_msg('Create noise timelines ... ')
  st = time.time()
  
  yaw_jitter = pitch_jitter = frame_osf = None
  
  jitter_file = opt.aocs.PointingModel().replace("__path__", opt.__path__) 
  if hasattr(opt.aocs, 'pointing_rms'): 
    jit_rms = opt.aocs.pointing_rms()
  else:
    jit_rms = None
    
  yaw_jitter, pitch_jitter, frame_osf = exolib.pointing_jitter(jitter_file,
                     total_observing_time, frame_time, rms = jit_rms)
  
  if opt.aocs.pointing_scan_throw()>0:       
       pitch_jitter = exolib.pointing_add_scan(pitch_jitter,
                            scan_throw_arcsec=opt.aocs.pointing_scan_throw(), 
                            frame_time = frame_time, frame_osf = frame_osf, 
                            exposure_time = exposure_time)
    
  for key in channel.keys():
    
    key, noise,  outputPointingTl = channel_noise_estimator(channel[key], key, yaw_jitter, pitch_jitter, frame_osf, frame_time, opt)
    channel[key].noise = noise
    channel[key].outputPointingTl = outputPointingTl
    
  
  exosim_msg(' - execution time: {:.0f} msec.\n'.format(
  (time.time()-st)*1000.0))
Beispiel #3
0
 def __init__(self, wl, level=1.0):
   exosim_msg('Instantiate Zodi ... ')
   st = time.time()
   spectrum = level*(3.5e-14*exolib.planck(wl, 5500*pq.K) + 
                     exolib.planck(wl, 270*pq.K) * 3.58e-8)
   
   self.sed 		= sed.Sed(wl, spectrum )
   self.transmission 	= sed.Sed(wl, np.ones(wl.size))
   self.units         = 'W m**-2 sr**-1 micron**-1'
   exosim_msg(' - execution time: {:.0f} msec.\n'.format((time.time()-st)*1000.0))
Beispiel #4
0
    def __init__(self, wl, level=1.0):
        exosim_msg('Instantiate Zodi ... ')
        st = time.time()
        spectrum = level * (3.5e-14 * exolib.planck(wl, 5500 * pq.K) +
                            exolib.planck(wl, 270 * pq.K) * 3.58e-8)

        self.sed = sed.Sed(wl, spectrum)
        self.transmission = sed.Sed(wl, np.ones(wl.size))
        self.units = 'W m**-2 sr**-1 micron**-1'
        exosim_msg(' - execution time: {:.0f} msec.\n'.format(
            (time.time() - st) * 1000.0))
Beispiel #5
0
def run_exosim(opt=None):
  
  star, planet = exosim.modules.astroscene.run(opt)
  
  exosim_msg(' Stellar SED: {:s}\n'.format(os.path.basename(star.ph_filename)))
  exosim_msg(' Star luminosity {:s}\n'.format(star.luminosity))
  
  #Instanciate Zodi
  zodi = exosim.classes.zodiacal_light(opt.common.common_wl, level=1.0)
  
  exosim.exolib.sed_propagation(star.sed, zodi.transmission)
  #Run Instrument Model
  channel = exosim.modules.instrument.run(opt, star, planet, zodi)
  #Create Signal timelines
  frame_time, total_observing_time, exposure_time = exosim.modules.timeline_generator.run(opt, channel, planet)
  #Generate noise timelines
  exosim.modules.noise.run(opt, channel, frame_time, total_observing_time, exposure_time)
  #Save
  exosim.modules.output.run(opt, channel, planet)
  
  return star, planet, zodi, channel
Beispiel #6
0
def run_exosim(opt=None):

    star, planet = exosim.modules.astroscene.run(opt)

    exosim_msg(' Stellar SED: {:s}\n'.format(os.path.basename(
        star.ph_filename)))
    exosim_msg(' Star luminosity {:f}\n'.format(star.luminosity))

    #Instanciate Zodi
    zodi = exosim.classes.zodiacal_light(opt.common.common_wl, level=1.0)

    exosim.exolib.sed_propagation(star.sed, zodi.transmission)
    #Run Instrument Model
    channel = exosim.modules.instrument.run(opt, star, planet, zodi)
    #Create Signal timelines
    frame_time, total_observing_time, exposure_time = exosim.modules.timeline_generator.run(
        opt, channel, planet)
    #Generate noise timelines
    exosim.modules.noise.run(opt, channel, frame_time, total_observing_time,
                             exposure_time)
    #Save
    exosim.modules.output.run(opt, channel, planet)

    return star, planet, zodi, channel
Beispiel #7
0
    zodi = exosim.classes.zodiacal_light(opt.common.common_wl, level=1.0)

    exosim.exolib.sed_propagation(star.sed, zodi.transmission)
    #Run Instrument Model
    channel = exosim.modules.instrument.run(opt, star, planet, zodi)
    #Create Signal timelines
    frame_time, total_observing_time, exposure_time = exosim.modules.timeline_generator.run(
        opt, channel, planet)
    #Generate noise timelines
    exosim.modules.noise.run(opt, channel, frame_time, total_observing_time,
                             exposure_time)
    #Save
    exosim.modules.output.run(opt, channel, planet)

    return star, planet, zodi, channel


if __name__ == "__main__":

    xmlFileNameDefault = 'exosim_defaults.xml'

    xmlFileName = sys.argv[1] if len(sys.argv) > 1 else xmlFileNameDefault

    exosim_msg('Reading options from file ... \n')
    opt = exosim.Options(
        filename=xmlFileName).opt  #, default_path = exosim.__path__[0]).opt

    # modify_opt(opt)

    star, planet, zodi, channel = run_exosim(opt)
def run(opt, channel, planet):
  exosim_msg('Create signal-only timelines ... ')
  st = time.time()
  # Estimate simulation length. Needs to be in units of hours.
  T14 =   planet.get_t14(planet.planet.i.rescale(pq.rad),
		 planet.planet.a.rescale(pq.m), 
		 planet.planet.P.rescale(pq.s), 
		 planet.planet.R.rescale(pq.m), 
		 planet.planet.star.R.rescale(pq.m)).rescale(pq.hour)
  
  total_observing_time = T14*(1.0+opt.timeline.before_transit()+opt.timeline.after_transit())
  time_at_transit      = T14*(0.5+opt.timeline.before_transit())
  frame_time           = 1.0/opt.timeline.frame_rate()    # Frame exposure, CLK
      
  for key in channel.keys():
    
    # Having exposure_time here will allow to have different integration times 
    # for different focal planes.
    exposure_time   = opt.timeline.exposure_time() # Exposure time
    # Estimate NDR rates
    multiaccum     = opt.timeline.multiaccum()    # Number of NDRs per exposure
    allocated_time = (opt.timeline.nGND()+
		      opt.timeline.nNDR0()+
		      opt.timeline.nRST()) * frame_time
    NDR_time       = (exposure_time-allocated_time)/(multiaccum-1)
    nNDR           = np.ceil(NDR_time/frame_time).astype(np.int).take(0)
    
    # Estimate the base block of CLK cycles
    base = [opt.timeline.nGND().take(0), opt.timeline.nNDR0().take(0)]
    for x in xrange(multiaccum-1): base.append(nNDR)
    base.append(opt.timeline.nRST().take(0))
    
    # Recalculate exposure time and estimates how many exposures are needed
    exposure_time = sum(base)*frame_time
    number_of_exposures = np.ceil(
      (total_observing_time/exposure_time).simplified.take(0)).astype(np.int)
    total_observing_time = exposure_time*number_of_exposures
    frame_sequence=np.tile(base, number_of_exposures) # This is Nij
    time_sequence = frame_time * frame_sequence.cumsum() # This is Tij
    
    # Physical time of each NDR
    ndr_time = np.dstack([time_sequence[1+i::len(base)] \
      for i in xrange(multiaccum)]).flatten()*time_sequence.units
    # Number of frames contributing to each NDR
    ndr_sequence = np.dstack([frame_sequence[1+i::len(base)] \
      for i in xrange(multiaccum)]).flatten()
    # CLK counter of each NDR
    ndr_cumulative_sequence = (ndr_time/frame_time).astype(np.int).magnitude

    # Create the noise-less timeline
    channel[key].set_timeline(exposure_time,
			      frame_time,
			      ndr_time, 
			      ndr_sequence,
			      ndr_cumulative_sequence)
      
    
    # Apply lightcurve model
    cr    =  channel[key].planet.sed[::channel[key].osf]
    cr_wl =  channel[key].planet.wl[::channel[key].osf]
    
    isPrimaryTransit = True if opt.astroscene.transit_is_primary()=='True' else False
    
    channel[key].lc, z, i0, i1 = planet.get_light_curve(cr, cr_wl, 
							channel[key].ndr_time, 
							time_at_transit, 
							isPrimaryTransit)
    channel[key].set_z(z)
  
  
  exosim_msg(' - execution time: {:.0f} msec.\n'.format(
  (time.time()-st)*1000.0))
  return frame_time, total_observing_time, exposure_time
Beispiel #9
0
def run(opt, channel, planet):
    exosim_msg('Save to file ... ')
    st = time.time()
    out_path = os.path.expanduser(opt.common.ExoSimOutputPath().replace(
        '__path__', opt.__path__))

    if not os.path.exists(out_path):
        os.mkdir(out_path)

    existing_sims = glob.glob(os.path.join(out_path, 'sim_*'))

    if not existing_sims:
        # Empty list
        sim_number = 0
    else:
        sim_number = sorted([np.int(l.split('_')[-1])
                             for l in existing_sims])[-1]
        sim_number += 1

    out_path = os.path.join(out_path, 'sim_{:04d}'.format(sim_number))
    os.mkdir(out_path)

    for key in channel.keys():
        n_ndr = channel[key].tl_shape[-1]
        multiaccum = np.int(opt.timeline.multiaccum())
        n_exp = n_ndr // multiaccum
        hdu = fits.PrimaryHDU()
        hdu.header['NEXP'] = (n_exp, 'Number of exposures')
        hdu.header['MACCUM'] = (multiaccum, 'Multiaccum')
        hdu.header['TEXP'] = (channel[key].exposure_time.item(),
                              'Exp Time [s]')
        hdu.header['PLANET'] = (planet.planet.name, 'Planet name')
        hdu.header['STAR'] = (planet.planet.star.name, 'Star name')

        #### Detector Simulation Values
        hdu.header['POINTRMS'] = (opt.aocs.pointing_rms.val.item(),
                                  'Model Pointing RMS')
        tempVal = list(
            filter(lambda x: x.name == key,
                   opt.channel))[0].detector_pixel.Idc.val.magnitude.item()
        hdu.header['DET_I_DC'] = (tempVal, 'Detector dark current')
        tempVal = list(filter(
            lambda x: x.name == key,
            opt.channel))[0].detector_pixel.sigma_ro.val.magnitude.item()
        hdu.header['DETROERR'] = (tempVal, 'Detector readout noise in e-rms')
        tempVal = list(filter(lambda x: x.name == key,
                              opt.channel))[0].plate_scale.val.rescale(
                                  'degree').magnitude.item()
        hdu.header['CDELT1'] = (tempVal, 'Degrees/pixel')
        hdu.header['CDELT2'] = (tempVal, 'Degrees/pixel')
        hdulist = fits.HDUList(hdu)

        for i in range(channel[key].tl_shape[-1]):
            hdu = fits.ImageHDU(channel[key].noise[..., i].astype(np.float32),
                                name='NOISE')
            hdu.header['EXPNUM'] = (i // multiaccum, 'Exposure Number')
            hdu.header['ENDRNUM'] = (i % multiaccum, 'NDR Number')
            hdu.header['EXTNAME'] = 'NOISE'
            hdulist.append(hdu)

        # Create column data
        col1 = fits.Column(
            name='Wavelength {:f}'.format(channel[key].wl_solution.units),
            format='E',
            array=channel[key].wl_solution[channel[key].offs::channel[key].
                                           osf])
        col2 = fits.Column(
            name='Input Contrast Ratio',
            format='E',
            array=channel[key].planet.sed[channel[key].offs::channel[key].osf])
        col3 = fits.Column(
            name='Stellar SED',
            format='E',
            array=channel[key].star.sed[channel[key].offs::channel[key].osf])
        cols = fits.ColDefs([col1, col2, col3])
        tbhdu = fits.BinTableHDU.from_columns(cols)
        tbhdu.name = 'INPUTS'
        hdulist.append(tbhdu)
        ########
        hdu = fits.ImageHDU(channel[key].outputPointingTl, name='SIM_POINTING')
        hdulist.append(hdu)
        ##############
        hdu = fits.ImageHDU(channel[key].ldc, name='LD_COEFF')
        hdulist.append(hdu)

        ############
        col1 = fits.Column(name='Time {:f}'.format(
            channel[key].ndr_time.units),
                           format='E',
                           array=channel[key].ndr_time)
        col2 = fits.Column(name='z', format='E', array=channel[key].z)

        cols = fits.ColDefs([col1, col2])
        tbhdu = fits.BinTableHDU.from_columns(cols)
        tbhdu.name = 'TIME'
        hdulist.append(tbhdu)
        #########

        hdu = fits.ImageHDU(channel[key].lc, name='LIGHT_CURVES')
        hdulist.append(hdu)

        #print hdulist
        hdulist.writeto(os.path.join(out_path, '{:s}_signal.fits'.format(key)))

    exosim_msg(' - execution time: {:.0f} msec.\n'.format(
        (time.time() - st) * 1000.0))
Beispiel #10
0
def run(opt, star, planet, zodi):

    exosim_msg('Run instrument model ... ')
    st = time.time()
    instrument_emission     = Sed(star.sed.wl,
                                  np.zeros(star.sed.wl.size, dtype=np.float64)* \
                                  pq.W/pq.m**2/pq.um/pq.sr)
    instrument_transmission = Sed(star.sed.wl,
                                  np.ones(star.sed.wl.size, dtype=np.float64))

    for op in opt.common_optics.optical_surface:
        dtmp_tr = np.loadtxt(op.transmission.replace('__path__', opt.__path__),
                             delimiter=',')
        dtmp_em = np.loadtxt(op.emissivity.replace('__path__', opt.__path__),
                             delimiter=',')

        tr = Sed(dtmp_tr[:, 0] * pq.um, dtmp_tr[:, 1] * pq.dimensionless)
        tr.rebin(opt.common.common_wl)

        em = Sed(dtmp_em[:, 0] * pq.um, dtmp_em[:, 1] * pq.dimensionless)
        em.rebin(opt.common.common_wl)

        exolib.sed_propagation(star.sed, tr)
        exolib.sed_propagation(zodi.sed, tr)
        exolib.sed_propagation(instrument_emission,
                               tr,
                               emissivity=em,
                               temperature=op())

        instrument_transmission.sed = instrument_transmission.sed * tr.sed

    channel = {}
    for ch in opt.channel:
        #if ch.name != 'NIR Spec': continue
        channel[ch.name] = Channel(star.sed,
                                   planet.cr,
                                   zodi.sed,
                                   instrument_emission,
                                   instrument_transmission,
                                   options=ch)

        ch_optical_surface = ch.optical_surface if isinstance(ch.optical_surface, list) else \
          [ch.optical_surface]
        for op in ch.optical_surface:
            dtmp = np.loadtxt(op.transmission.replace('__path__',
                                                      opt.__path__),
                              delimiter=',')
            tr = Sed(dtmp[:,0]*pq.um, \
                    dtmp[:,1]*pq.dimensionless)
            tr.rebin(opt.common.common_wl)
            em = Sed(dtmp[:,0]*pq.um, \
                     dtmp[:,2]*pq.dimensionless)
            em.rebin(opt.common.common_wl)
            exolib.sed_propagation(channel[ch.name].star, tr)
            exolib.sed_propagation(channel[ch.name].zodi, tr)
            exolib.sed_propagation(channel[ch.name].emission, \
                    tr, emissivity=em,temperature=op())
            channel[ch.name].transmission.sed *= tr.sed

        # BUG workaround. There is a bug in the binning function. If transmission is zero,
        # it is rebiined to a finite, very small value. This needs to be fixed!
        # For now, I set to zero all transmission smaller than an arbitrary value
        #idx = np.where(channel[ch.name].transmission.sed < 1.0e-5)
        #channel[ch.name].star.sed[idx] = 0.0*channel[ch.name].star.sed.units
        #channel[ch.name].zodi.sed[idx] = 0.0*channel[ch.name].zodi.sed.units
        #channel[ch.name].emission.sed[idx] = 0.0*channel[ch.name].emission.sed.units
        #channel[ch.name].transmission.sed[idx] = 0.0*channel[ch.name].transmission.sed.units

        # Convert spectral signals
        dtmp = np.loadtxt(ch.qe().replace('__path__', opt.__path__),
                          delimiter=',')
        qe = Sed(dtmp[:,0]*pq.um, \
                     dtmp[:,1]*pq.dimensionless)

        Responsivity = qe.sed * qe.wl.rescale(
            pq.m) / (spc.c * spc.h * pq.m * pq.J) * pq.UnitQuantity(
                'electron', symbol='e-')

        Re = scipy.interpolate.interp1d(qe.wl, Responsivity)

        Aeff = 0.25 * np.pi * opt.common_optics.TelescopeEffectiveDiameter()**2
        Omega_pix = 2.0 * np.pi * (1.0 -
                                   np.cos(np.arctan(0.5 / ch.wfno()))) * pq.sr
        Apix = ch.detector_pixel.pixel_size()**2
        channel[ch.name].star.sed     *= Aeff             * \
          Re(channel[ch.name].star.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
        channel[ch.name].zodi.sed     *= Apix * Omega_pix * \
          Re(channel[ch.name].zodi.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
        channel[ch.name].emission.sed *= Apix * Omega_pix * \
          Re(channel[ch.name].emission.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J

        ### create focal plane

        #1# allocate focal plane with pixel oversampling such that Nyquist sampling is done correctly
        fpn = ch.array_geometry()
        fp = np.zeros((fpn * ch.osf()).astype(np.int))

        #2# This is the current sampling interval in the focal plane.
        fp_delta = ch.detector_pixel.pixel_size() / ch.osf()

        #3# Load dispersion law
        if ch.type == 'spectrometer':
            if hasattr(ch, "dispersion"):
                dtmp = np.loadtxt(ch.dispersion.path.replace(
                    '__path__', opt.__path__),
                                  delimiter=',')
                ld = scipy.interpolate.interp1d(dtmp[..., 2] * pq.um +
                                                ch.dispersion().rescale(pq.um),
                                                dtmp[..., 0],
                                                bounds_error=False,
                                                kind='slinear',
                                                fill_value=0.0)
            elif hasattr(ch, "ld"):
                # wl = ld[0] + ld[1](x - ld[2]) = ld[1]*x + ld[0]-ldp[1]*ld[2]
                ld = np.poly1d(
                    (ch.ld()[1], ch.ld()[0] - ch.ld()[1] * ch.ld()[2]))
            else:
                exolib.exosim_error("Dispersion law not defined.")

            #4a# Estimate pixel and wavelength coordinates
            x_pix_osr = np.arange(fp.shape[1]) * fp_delta
            x_wav_osr = ld(x_pix_osr.rescale(
                pq.um).magnitude) * pq.um  # walength on each x pixel
            channel[ch.name].wl_solution = x_wav_osr

        elif ch.type == 'photometer':
            #4b# Estimate pixel and wavelength coordinates
            idx = np.where(channel[ch.name].transmission.sed >
                           channel[ch.name].transmission.sed.max() / np.e)
            x_wav_osr = np.linspace(
                channel[ch.name].transmission.wl[idx].min().item(),
                channel[ch.name].transmission.wl[idx].max().item(),
                8 * ch.osf()) * channel[ch.name].transmission.wl.units
            x_wav_center = (channel[ch.name].transmission.wl[idx]*channel[ch.name].transmission.sed[idx]).sum() / \
              channel[ch.name].transmission.sed[idx].sum()

            channel[ch.name].wl_solution = np.repeat(x_wav_center, fp.shape[1])

        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

        d_x_wav_osr = np.zeros_like(x_wav_osr)
        idx = np.where(x_wav_osr > 0.0)
        d_x_wav_osr[idx] = np.gradient(x_wav_osr[idx])
        if np.any(d_x_wav_osr < 0): d_x_wav_osr *= -1.0

        #5# Generate PSFs, one in each detector pixel along spectral axis
        psf = exolib.Psf(x_wav_osr, ch.wfno(), \
                        fp_delta, shape='airy')

        #6# Save results in Channel class
        channel[ch.name].fp_delta = fp_delta
        channel[ch.name].psf = psf
        channel[ch.name].fp = fp
        channel[ch.name].osf = np.int(ch.osf())
        channel[ch.name].offs = np.int(ch.pix_offs())

        channel[ch.name].planet.sed *= channel[ch.name].star.sed
        channel[ch.name].star.rebin(x_wav_osr)
        channel[ch.name].planet.rebin(x_wav_osr)
        channel[ch.name].zodi.rebin(x_wav_osr)
        channel[ch.name].emission.rebin(x_wav_osr)
        channel[ch.name].transmission.rebin(x_wav_osr)
        channel[ch.name].star.sed *= d_x_wav_osr
        channel[ch.name].planet.sed *= d_x_wav_osr
        channel[ch.name].zodi.sed *= d_x_wav_osr
        channel[ch.name].emission.sed *= d_x_wav_osr

        #7# Populate focal plane with monochromatic PSFs
        if ch.type == 'spectrometer':
            j0 = np.round(np.arange(fp.shape[1]) - psf.shape[1] // 2).astype(
                np.int)

        elif ch.type == 'photometer':
            j0 = np.repeat(fp.shape[1] // 2, x_wav_osr.size)
        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

        j1 = j0 + psf.shape[1]
        idx = np.where((j0 >= 0) & (j1 < fp.shape[1]))[0]
        i0 = fp.shape[0] // 2 - psf.shape[0] // 2 + channel[ch.name].offs
        i1 = i0 + psf.shape[0]
        for k in idx:            channel[ch.name].fp[i0:i1, j0[k]:j1[k]] += psf[...,k] * \
  channel[ch.name].star.sed[k]

        #9# Now deal with the planet
        planet_response = np.zeros(fp.shape[1])
        i0p = np.unravel_index(np.argmax(channel[ch.name].psf.sum(axis=2)),
                               channel[ch.name].psf[..., 0].shape)[0]
        for k in idx:
            planet_response[j0[k]:j1[k]] += psf[i0p, :, k] * channel[
                ch.name].planet.sed[k]

        #9# Allocate pixel response function
        kernel, kernel_delta = exolib.PixelResponseFunction(
            channel[ch.name].psf.shape[0:2],
            7 * ch.osf(),  # NEED TO CHANGE FACTOR OF 7 
            ch.detector_pixel.pixel_size(),
            lx=ch.detector_pixel.pixel_diffusion_length())

        channel[ch.name].fp = exolib.fast_convolution(
            channel[ch.name].fp, channel[ch.name].fp_delta, kernel,
            kernel_delta)

        ## TODO CHANGE THIS: need to convolve planet with pixel response function
        channel[ch.name].planet = Sed(
            channel[ch.name].wl_solution,
            planet_response / (1e-30 + fp[(i0 + i1) // 2, ...]))

        ## Fix units
        channel[
            ch.name].fp = channel[ch.name].fp * channel[ch.name].star.sed.units
        channel[ch.name].planet.sed = channel[
            ch.name].planet.sed * pq.dimensionless

        ## Deal with diffuse radiation
        if ch.type == 'spectrometer':
            channel[ch.name].zodi.sed = scipy.convolve(
                channel[ch.name].zodi.sed,
                np.ones(np.int(ch.slit_width() * channel[ch.name].opt.osf())),
                'same') * channel[ch.name].zodi.sed.units
            channel[ch.name].emission.sed = scipy.convolve(
                channel[ch.name].emission.sed,
                np.ones(np.int(ch.slit_width() * channel[ch.name].opt.osf())),
                'same') * channel[ch.name].emission.sed.units
        elif ch.type == 'photometer':
            channel[ch.name].zodi.sed = np.repeat(
                channel[ch.name].zodi.sed.sum(),
                channel[ch.name].wl_solution.size)
            channel[ch.name].zodi.wl = channel[ch.name].wl_solution
            channel[ch.name].emission.sed = np.repeat(
                channel[ch.name].emission.sed.sum(),
                channel[ch.name].wl_solution.size)
            channel[ch.name].emission.wl = channel[ch.name].wl_solution

        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

    exosim_msg(' - execution time: {:.0f} msec.\n'.format(
        (time.time() - st) * 1000.0))
    return channel

    pass
Beispiel #11
0
def create_jitter_noise(channel,
                        x_jit,
                        y_jit,
                        frame_osf,
                        frame_time,
                        key,
                        opt,
                        visualize=False):

    outputPointingTL = create_output_pointing_timeline(
        x_jit, y_jit, frame_osf, ndrCumSeq=channel.ndr_cumulative_sequence)

    jitter_x = channel.osf * (x_jit / channel.opt.plate_scale()).simplified
    jitter_y = channel.osf * (y_jit / channel.opt.plate_scale()).simplified

    fp_units = channel.fp.units
    fp = channel.fp.magnitude
    osf = np.int32(channel.osf)
    offs = np.int32(channel.offs)

    magnification_factor = np.ceil(
        max(3.0 / jitter_x.std(), 3.0 / jitter_y.std()))

    if (magnification_factor > 1):
        try:
            mag = np.int(magnification_factor.item()) | 1
        except:
            mag = np.int(magnification_factor) | 1

        fp = exolib.oversample(fp, mag)

        #### See ExoSim Issue 42, for following.
        #    fp = np.where(fp >= 0.0, fp, 1e-10)

        osf *= mag
        offs = mag * offs + mag // 2
        jitter_x *= mag
        jitter_y *= mag

    if opt.noise.EnableSpatialJitter() != 'True': jitter_y *= 0.0
    if opt.noise.EnableSpectralJitter() != 'True': jitter_x *= 0.0

    jitter_x = np.round(jitter_x)
    jitter_y = np.round(jitter_y)
    noise = np.zeros((int(fp.shape[0] // osf), int(fp.shape[1] // osf),
                      0)).astype(np.float32)

    # multiple orders reshaping
    if (hasattr(channel.opt, "dispersion")
            and (isinstance(channel.opt.dispersion, list))):
        exosim_msg(
            "Using Dask for parellized processing of timeline datacube...")
        cores = opt.common.num_cores
        lim = cores // len(channel.opt.dispersion)
        indxRanges = np.arange(0, lim + 1) * channel.tl_shape[2] // lim

        # need to separate the "fake" focal planes so that they don't bleed into one another
        # when jitter is applied.

        client = Client(n_workers=cores,
                        memory_limit=f'{opt.common.gb_per_core}GB')
        temp_shape = (noise.shape[0],
                      noise.shape[1] // len(channel.opt.dispersion), 0)
        fp = np.array(np.split(fp, len(channel.opt.dispersion), 1))
        results = []
        for d, _ in enumerate(channel.opt.dispersion):
            jitters = []
            for i in range(len(indxRanges) - 1):
                startIdx = int(indxRanges[i])
                endIdx = int(indxRanges[i + 1])

                tfp = fp[d].astype(np.float32)
                tosf = osf.astype(np.int32)
                tndr = channel.ndr_sequence[startIdx:endIdx].astype(np.int32)
                tndrs = channel.ndr_cumulative_sequence[
                    startIdx:endIdx].astype(np.int32)
                tfosf = frame_osf.astype(np.int32)
                tjitx = jitter_x.magnitude.astype(np.int32)
                tjity = jitter_y.magnitude.astype(np.int32)
                txoff = offs.astype(np.int32)
                tyoff = offs.astype(np.int32)

                jitter = delayed(c_create_jitter_noise)(tfp,
                                                        tosf,
                                                        tndr,
                                                        tndrs,
                                                        tfosf,
                                                        tjitx,
                                                        tjity,
                                                        x_offs=txoff,
                                                        y_offs=tyoff)
                jitters.append(jitter)

            result = da.concatenate([
                da.from_delayed(
                    jit,
                    dtype='float32',
                    shape=(temp_shape[0], temp_shape[1], endIdx - startIdx))
                for jit in jitters
            ],
                                    axis=2)
            results.append(result)
        noise = da.stack(results, axis=0)

        noise = da.concatenate((noise[da.arange(noise.shape[0])]), axis=1)
        if visualize:
            noise.visualize(filename='exosim-dask-noise.png')
        noise = noise.compute()
        client.close()

    else:
        indxRanges = np.arange(0, 7) * channel.tl_shape[2] // 6

    for i in range(len(indxRanges) - 1):
        startIdx = int(indxRanges[i])
        endIdx = int(indxRanges[i + 1])
        noise = np.append(
            noise,
            c_create_jitter_noise(
                fp.astype(np.float32),
                osf.astype(np.int32),
                channel.ndr_sequence[startIdx:endIdx].astype(np.int32),
                channel.ndr_cumulative_sequence[startIdx:endIdx].astype(
                    np.int32),
                frame_osf.astype(np.int32),
                jitter_x.magnitude.astype(np.int32),
                jitter_y.magnitude.astype(np.int32),
                x_offs=offs.astype(np.int32),
                y_offs=offs.astype(np.int32)).astype(np.float32),
            axis=2)

    ## Multiply units to noise in 2 steps, to avoid
    ##        Quantities memmory inefficiency
    qq = channel.ndr_sequence * fp_units * frame_time
    noise = noise * qq
    return noise, outputPointingTL
Beispiel #12
0
  exosim_msg(' Stellar SED: {:s}\n'.format(os.path.basename(star.ph_filename)))
  exosim_msg(' Star luminosity {:s}\n'.format(star.luminosity))
  
  #Instanciate Zodi
  zodi = exosim.classes.zodiacal_light(opt.common.common_wl, level=1.0)
  
  exosim.exolib.sed_propagation(star.sed, zodi.transmission)
  #Run Instrument Model
  channel = exosim.modules.instrument.run(opt, star, planet, zodi)
  #Create Signal timelines
  frame_time, total_observing_time, exposure_time = exosim.modules.timeline_generator.run(opt, channel, planet)
  #Generate noise timelines
  exosim.modules.noise.run(opt, channel, frame_time, total_observing_time, exposure_time)
  #Save
  exosim.modules.output.run(opt, channel, planet)

  return star, planet, zodi, channel
  
if __name__ == "__main__":
  
  xmlFileNameDefault = 'exosim_twinkle_ep.xml' 
  
  xmlFileName = sys.argv[1]  if len(sys.argv)>1 else xmlFileNameDefault
  
  exosim_msg('Reading options from file ... \n')
  opt = exosim.Options(filename=xmlFileName).opt #, default_path = exosim.__path__[0]).opt
  
  # modify_opt(opt)
  
  star, planet, zodi, channel = run_exosim(opt)
  #pass
Beispiel #13
0
def run(opt, channel, planet):
  exosim_msg('Create signal-only timelines ... ')
  st = time.time()
  # Estimate simulation length. Needs to be in units of hours.
  T14 =   planet.get_t14(planet.planet.i.rescale(pq.rad),
     planet.planet.a.rescale(pq.m), 
     planet.planet.P.rescale(pq.s), 
     planet.planet.R.rescale(pq.m), 
     planet.planet.star.R.rescale(pq.m)).rescale(pq.hour)
  
  total_observing_time = T14*(1.0+opt.timeline.before_transit()+opt.timeline.after_transit())
  time_at_transit      = T14*(0.5+opt.timeline.before_transit())
  frame_time           = 1.0/opt.timeline.frame_rate()    # Frame exposure, CLK
      
      
      
  peak1Max = []
  peak2Max = []
  
  
  ##### FOR TESTING - TO BE DELETED
  ADD_STELAR_VARIABILITY = False
  if ADD_STELAR_VARIABILITY:
    workDir = '/home/alp/Work/Ariel/StarVarModels/'
    modelFile = 'M2V_SED_Timeseries.fits'
    fileName = workDir + modelFile
    wlArr, tArr, lc = loadExternalLightCurve(fileName)
    print('qqqqqqqqqqqqqqqqqqqqqqqq===================',lc.shape)
    
    from scipy import interpolate
    f = interpolate.interp2d(wlArr, tArr, lc, kind='cubic')
    
    #qqqq
  
  ##### TESTING END - TO BE DELETED
  
  
  for key in channel.keys():
    
    # Having exposure_time here will allow to have different integration times 
    # for different focal planes.
    exposure_time   = opt.timeline.exposure_time() # Exposure time
    # Estimate NDR rates
    multiaccum     = opt.timeline.multiaccum()    # Number of NDRs per exposure
    allocated_time = (opt.timeline.nGND()+
          opt.timeline.nNDR0()+
          opt.timeline.nRST()) * frame_time
    NDR_time       = (exposure_time-allocated_time)/(multiaccum-1)
    nNDR           = np.ceil(NDR_time/frame_time).astype(np.int).take(0)
    
    # Estimate the base block of CLK cycles
    base = [opt.timeline.nGND().take(0), opt.timeline.nNDR0().take(0)]
    for x in range(int(multiaccum)-1): base.append(nNDR)
    base.append(opt.timeline.nRST().take(0))
    
    # Recalculate exposure time and estimates how many exposures are needed
    exposure_time = sum(base)*frame_time
    number_of_exposures = np.ceil(
      (total_observing_time/exposure_time).simplified.take(0)).astype(np.int)
    total_observing_time = exposure_time*number_of_exposures
    frame_sequence=np.tile(base, number_of_exposures) # This is Nij
    time_sequence = frame_time * frame_sequence.cumsum() # This is Tij
    
    # Physical time of each NDR
    ndr_time = np.dstack([time_sequence[1+i::len(base)] \
      for i in range(int(multiaccum))]).flatten()*time_sequence.units
    # Number of frames contributing to each NDR
    ndr_sequence = np.dstack([frame_sequence[1+i::len(base)] \
      for i in range(int(multiaccum))]).flatten()
    # CLK counter of each NDR
    ndr_cumulative_sequence = (ndr_time/frame_time).astype(np.int).magnitude

    # Create the noise-less timeline
    channel[key].set_timeline(exposure_time,
            frame_time,
            ndr_time, 
            ndr_sequence,
            ndr_cumulative_sequence)
      
    
    # Apply lightcurve model
    cr    =  channel[key].planet.sed[::channel[key].osf]
    cr_wl =  channel[key].planet.wl[::channel[key].osf]
    
    isPrimaryTransit = True if opt.astroscene.transit_is_primary()=='True' else False
    apply_phase_curve = True if opt.astroscene.apply_phase_curve()=='True' else False

    channel[key].lc, z, i0, i1 = planet.get_light_curve(cr, cr_wl, 
              channel[key].ndr_time, 
              time_at_transit, 
              isPrimaryTransit,
              apply_phase_curve)
    
    
    
    channel[key].ldc = planet.u

    ## ADD StelarVariability Model here
    ## Temporary TEST block
    if ADD_STELAR_VARIABILITY:
      tInterp = channel[key].ndr_time - channel[key].ndr_time[0] 
      wlInterp = cr_wl
      lcCorr = f(wlInterp[::-1], tInterp)[::-1, :]
      print() 
      print('\nCR_WL MIN/MAX', np.min(cr_wl), np.max(cr_wl))
      print('=================================', f(0.93345514, 0))
      print('=================================', f(3, 0))
      
      print('##############################')
      print('##############################')
      print('##  APPLYING STELLAR VAR NOISE')
      print('##############################')
      print('##############################')

      import matplotlib.pyplot as plt
      plt.plot(lcCorr)
      plt.show()
      channel[key].lc  *=lcCorr.T
    
    
    
    
    ## ADD SpotSim LC correction here as one line.
    ## Temporary TEST block
    if False:
      print('QQQQQQQQQQQQQQQ MODDED LD CoEFFS')
      channel[key].ldc = np.zeros_like(channel[key].ldc)
      print('QQQQQQQQQQQQQQQ MODDED LD CoEFFS')
      
      spotsList2 = [
         {'r':3, 'x': 27.65, 'y':23.63},
         {'r':6, 'x':-27.41, 'y':-0.19},
         {'r':6, 'x':-30.41, 'y':23.19},
         {'r':7, 'x': -76.41, 'y':-0.19},
         ]
      spotParams = {
          'CONTspot':800, 'CONTfac':100, 'Q':1.6
          }
      Tstar = float(planet.planet.star.T)
      a = float(planet.planet.a.rescale('m'))
      b = np.cos(np.radians(float(planet.planet.i)))*\
            float(planet.planet.a.rescale('m'))/float(planet.planet.star.R.rescale('m'))
      exosim_msg('\n')
      SM = SpotSim2(spotsList2, spotParams, Tstar, a, b, pix=200)
      lc_spotted = np.ones_like(channel[key].lc)
      for i in range(len(cr_wl)):
        exosim_msg('SpotSim - %s - %d/%d\r'%(key, i+1, len(cr_wl)))
        c1 = channel[key].ldc[i, 0]
        c2 = channel[key].ldc[i, 1]
        if  float(cr[i])>0:
          qqqlc, qqqlc_i = SM.calcLC(float(cr_wl[i]), float(cr[i]), c1, c2, z)
          lc_spotted[i, :] = qqqlc
      
      import matplotlib.pyplot as plt
      
      
      tPlot = channel[key].ndr_time
      for i in range(len(cr_wl)):
        if i%(2*np.round((len(cr)/32))**1)==0 and np.mean(lc_spotted[i, :])<.999999:
          yVals = lc_spotted[i, :]# - channel[key].lc[i, :]
          offset = float(np.mean(yVals[(tPlot>3000) & ((tPlot<3300))]))
          peak1Max.append([cr_wl[i], float(np.max((yVals-offset)[(tPlot>2200) & ((tPlot<2700))]))])
          peak2Max.append([cr_wl[i], float(np.max((yVals-offset)[(tPlot>3400) & ((tPlot<3800))]))])
          
          plt.figure(1)
          plt.plot(tPlot,  yVals, label = '%.2f um'%cr_wl[i])
          #plt.plot(tPlot,  lc_spotted[i, :], label = '%.2f um'%cr_wl[i])
          
          if key.startswith('FGS'):
            break
    
    
    
    
    channel[key].set_z(z)
    
  
  
  exosim_msg(' - execution time: {:.0f} msec.\n'.format(
  (time.time()-st)*1000.0))
  return frame_time, total_observing_time, exposure_time
Beispiel #14
0
def run(opt, star, planet, zodi):
  
  exosim_msg('Run instrument model ... ')
  st = time.time()
  instrument_emission  = Sed(star.sed.wl, 
                             np.zeros(star.sed.wl.size, dtype=np.float64)* \
                             pq.W/pq.m**2/pq.um/pq.sr)
  instrument_transmission = Sed(star.sed.wl, np.ones(star.sed.wl.size, dtype=np.float64))

  for op in opt.common_optics.optical_surface:
    dtmp=np.loadtxt(op.transmission.replace('__path__', opt.__path__), delimiter=',')

    tr = Sed(dtmp[:,0]*pq.um,dtmp[:,1]*pq.dimensionless)
    tr.rebin(opt.common.common_wl)

    em = Sed(dtmp[:,0]*pq.um,dtmp[:,2]*pq.dimensionless)
    em.rebin(opt.common.common_wl)
    
    exolib.sed_propagation(star.sed, tr)
    exolib.sed_propagation(zodi.sed, tr)
    exolib.sed_propagation(instrument_emission, tr, emissivity=em, temperature=op())
    instrument_transmission.sed = instrument_transmission.sed*tr.sed

    
  channel = {}
  for ch in opt.channel:
    #if ch.name != 'NIR Spec': continue
    channel[ch.name] = Channel(star.sed, planet.cr, 
			       zodi.sed, 
			       instrument_emission,   
			       instrument_transmission,
			       options=ch)
    
    ch_optical_surface = ch.optical_surface if isinstance(ch.optical_surface, list) else \
      [ch.optical_surface]
    for op in ch.optical_surface:
      dtmp=np.loadtxt(op.transmission.replace(
          '__path__', opt.__path__), delimiter=',')
      tr = Sed(dtmp[:,0]*pq.um, \
              dtmp[:,1]*pq.dimensionless)
      tr.rebin(opt.common.common_wl)
      em = Sed(dtmp[:,0]*pq.um, \
               dtmp[:,2]*pq.dimensionless)
      em.rebin(opt.common.common_wl)
      exolib.sed_propagation(channel[ch.name].star, tr)
      exolib.sed_propagation(channel[ch.name].zodi, tr)
      exolib.sed_propagation(channel[ch.name].emission, \
              tr, emissivity=em,temperature=op())
      channel[ch.name].transmission.sed *= tr.sed

    # BUG workaround. There is a bug in the binning function. If transmission is zero,
    # it is rebiined to a finite, very small value. This needs to be fixed!
    # For now, I set to zero all transmission smaller than an arbitrary value
    #idx = np.where(channel[ch.name].transmission.sed < 1.0e-5)
    #channel[ch.name].star.sed[idx] = 0.0*channel[ch.name].star.sed.units
    #channel[ch.name].zodi.sed[idx] = 0.0*channel[ch.name].zodi.sed.units
    #channel[ch.name].emission.sed[idx] = 0.0*channel[ch.name].emission.sed.units
    #channel[ch.name].transmission.sed[idx] = 0.0*channel[ch.name].transmission.sed.units
    
    
    # Convert spectral signals
    dtmp=np.loadtxt(ch.qe().replace(
	    '__path__', opt.__path__), delimiter=',')
    qe = Sed(dtmp[:,0]*pq.um, \
		 dtmp[:,1]*pq.dimensionless)
    
    Responsivity = qe.sed * qe.wl.rescale(pq.m)/(spc.c * spc.h * pq.m * pq.J)*pq.UnitQuantity('electron', symbol='e-')
    
    Re = scipy.interpolate.interp1d(qe.wl, Responsivity)
    
    Aeff = 0.25*np.pi*opt.common_optics.TelescopeEffectiveDiameter()**2
    Omega_pix = 2.0*np.pi*(1.0-np.cos(np.arctan(0.5/ch.wfno())))*pq.sr
    Apix = ch.detector_pixel.pixel_size()**2
    channel[ch.name].star.sed     *= Aeff             * \
      Re(channel[ch.name].star.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
    channel[ch.name].zodi.sed     *= Apix * Omega_pix * \
      Re(channel[ch.name].zodi.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
    channel[ch.name].emission.sed *= Apix * Omega_pix * \
      Re(channel[ch.name].emission.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
    
    ### create focal plane
    
    #1# allocate focal plane with pixel oversampling such that Nyquist sampling is done correctly 
    fpn = ch.array_geometry()
    fp  = np.zeros( (fpn*ch.osf()).astype(np.int) )

    #2# This is the current sampling interval in the focal plane.  
    fp_delta = ch.detector_pixel.pixel_size() / ch.osf()
    
    
    #3# Load dispersion law 
    if ch.type == 'spectrometer':
      if hasattr(ch, "dispersion"):
	dtmp=np.loadtxt(ch.dispersion.path.replace(
	  '__path__', opt.__path__), delimiter=',')
	ld = scipy.interpolate.interp1d(dtmp[...,2]*pq.um + ch.dispersion().rescale(pq.um), 
					dtmp[...,0],
					bounds_error=False, 
					fill_value=0.0)
      elif hasattr(ch, "ld"):
	# wl = ld[0] + ld[1](x - ld[2]) = ld[1]*x + ld[0]-ldp[1]*ld[2]
	ld = np.poly1d( (ch.ld()[1], ch.ld()[0]-ch.ld()[1]*ch.ld()[2]) )
      else:
	exolib.exosim_error("Dispersion law not defined.")
      
      #4a# Estimate pixel and wavelength coordinates
      x_pix_osr = np.arange(fp.shape[1]) * fp_delta  
      x_wav_osr = ld(x_pix_osr.rescale(pq.um))*pq.um # walength on each x pixel
      channel[ch.name].wl_solution = x_wav_osr
    
    elif ch.type == 'photometer':
      #4b# Estimate pixel and wavelength coordinates
      idx = np.where(channel[ch.name].transmission.sed > channel[ch.name].transmission.sed.max()/np.e)
      x_wav_osr = np.linspace(channel[ch.name].transmission.wl[idx].min().item(),
			      channel[ch.name].transmission.wl[idx].max().item(),
			      8 * ch.osf()) * channel[ch.name].transmission.wl.units
      x_wav_center = (channel[ch.name].transmission.wl[idx]*channel[ch.name].transmission.sed[idx]).sum() / \
	channel[ch.name].transmission.sed[idx].sum()
      
      channel[ch.name].wl_solution = np.repeat(x_wav_center, fp.shape[1])
      
    
    else:
      exolib.exosim_error("Channel should be either photometer or spectrometer.")
      
    d_x_wav_osr = np.zeros_like (x_wav_osr)
    idx = np.where(x_wav_osr > 0.0)
    d_x_wav_osr[idx] = np.gradient(x_wav_osr[idx])
    if np.any(d_x_wav_osr < 0): d_x_wav_osr *= -1.0

    #5# Generate PSFs, one in each detector pixel along spectral axis
    psf = exolib.Psf(x_wav_osr, ch.wfno(), \
		    fp_delta, shape='airy') 
    
    #6# Save results in Channel class
    channel[ch.name].fp_delta    = fp_delta
    channel[ch.name].psf         = psf
    channel[ch.name].fp          = fp
    channel[ch.name].osf         = np.int(ch.osf())
    channel[ch.name].offs        = np.int(ch.pix_offs())
    
    channel[ch.name].planet.sed  *= channel[ch.name].star.sed
    channel[ch.name].star.rebin(x_wav_osr)
    channel[ch.name].planet.rebin(x_wav_osr)
    channel[ch.name].zodi.rebin(x_wav_osr)
    channel[ch.name].emission.rebin(x_wav_osr)
    channel[ch.name].transmission.rebin(x_wav_osr)
    channel[ch.name].star.sed     *= d_x_wav_osr
    channel[ch.name].planet.sed   *= d_x_wav_osr
    channel[ch.name].zodi.sed     *= d_x_wav_osr 
    channel[ch.name].emission.sed *= d_x_wav_osr
    
    #7# Populate focal plane with monochromatic PSFs
    if ch.type == 'spectrometer':
      j0 = np.round(np.arange(fp.shape[1]) - psf.shape[1]/2).astype(np.int) 
    
    elif ch.type == 'photometer':
      j0 = np.repeat(fp.shape[1]//2, x_wav_osr.size)
    else:
      exolib.exosim_error("Channel should be either photometer or spectrometer.")
      
    j1 = j0 + psf.shape[1]
    idx = np.where((j0>=0) & (j1 < fp.shape[1]))[0]
    i0 = fp.shape[0]/2 - psf.shape[0]/2 + channel[ch.name].offs 
    i1 = i0 + psf.shape[0]
    for k in idx: channel[ch.name].fp[i0:i1, j0[k]:j1[k]] += psf[...,k] * \
        channel[ch.name].star.sed[k]  

    
    #9# Now deal with the planet
    planet_response = np.zeros(fp.shape[1])
    i0p = np.unravel_index(np.argmax(channel[ch.name].psf.sum(axis=2)), channel[ch.name].psf[...,0].shape)[0]
    for k in idx: planet_response[j0[k]:j1[k]] += psf[i0p,:,k] * channel[ch.name].planet.sed[k] 
    
    #9# Allocate pixel response function
    kernel, kernel_delta = exolib.PixelResponseFunction(
        channel[ch.name].psf.shape[0:2],
        7*ch.osf(),   # NEED TO CHANGE FACTOR OF 7 
        ch.detector_pixel.pixel_size(),
        lx = ch.detector_pixel.pixel_diffusion_length())

    channel[ch.name].fp = exolib.fast_convolution(
        channel[ch.name].fp, 
        channel[ch.name].fp_delta,
        kernel, kernel_delta)
  
    ## TODO CHANGE THIS: need to convolve planet with pixel response function
    channel[ch.name].planet = Sed(channel[ch.name].wl_solution, planet_response/(1e-30+fp[(i0+i1)//2, ...]))
    
    ## Fix units
    channel[ch.name].fp = channel[ch.name].fp*channel[ch.name].star.sed.units
    channel[ch.name].planet.sed = channel[ch.name].planet.sed*pq.dimensionless
    
    ## Deal with diffuse radiation
    if ch.type == 'spectrometer':
      channel[ch.name].zodi.sed     = scipy.convolve(channel[ch.name].zodi.sed, 
		      np.ones(np.int(ch.slit_width()*channel[ch.name].opt.osf())), 
		      'same') * channel[ch.name].zodi.sed.units
      channel[ch.name].emission.sed = scipy.convolve(channel[ch.name].emission.sed, 
		      np.ones(np.int(ch.slit_width()*channel[ch.name].opt.osf())), 
		      'same') * channel[ch.name].emission.sed.units
    elif ch.type == 'photometer':
      channel[ch.name].zodi.sed = np.repeat(channel[ch.name].zodi.sed.sum(),
					    channel[ch.name].wl_solution.size)
      channel[ch.name].zodi.wl = channel[ch.name].wl_solution
      channel[ch.name].emission.sed = np.repeat(channel[ch.name].emission.sed.sum(),
						channel[ch.name].wl_solution.size)
      channel[ch.name].emission.wl = channel[ch.name].wl_solution
      
    else:
      exolib.exosim_error("Channel should be either photometer or spectrometer.")
  
    channel[ch.name].fp += channel[ch.name].zodi.sed
    channel[ch.name].fp += channel[ch.name].emission.sed
    
    
  exosim_msg(' - execution time: {:.0f} msec.\n'.format((time.time()-st)*1000.0))
  return channel

  pass
Beispiel #15
0
def run(opt, star, planet, zodi):
    test_fp = False
    test_multord = False

    exosim_msg('Run instrument model ... ')
    st = time.time()
    instrument_emission     = Sed(star.sed.wl,
                                  np.zeros(star.sed.wl.size, dtype=np.float64)* \
                                  pq.W/pq.m**2/pq.um/pq.sr)
    instrument_transmission = Sed(star.sed.wl,
                                  np.ones(star.sed.wl.size, dtype=np.float64))

    for op in opt.common_optics.optical_surface:
        dtmp_tr = np.loadtxt(op.transmission.replace('__path__', opt.__path__),
                             delimiter=',')
        dtmp_em = np.loadtxt(op.emissivity.replace('__path__', opt.__path__),
                             delimiter=',')

        tr = Sed(dtmp_tr[:, 0] * pq.um, dtmp_tr[:, 1] * pq.dimensionless)
        tr.rebin(opt.common.common_wl)

        em = Sed(dtmp_em[:, 0] * pq.um, dtmp_em[:, 1] * pq.dimensionless)
        em.rebin(opt.common.common_wl)

        exolib.sed_propagation(star.sed, tr)
        exolib.sed_propagation(zodi.sed, tr)
        exolib.sed_propagation(instrument_emission,
                               tr,
                               emissivity=em,
                               temperature=op())

        instrument_transmission.sed = instrument_transmission.sed * tr.sed

    channel = {}
    for ch in opt.channel:
        channel[ch.name] = Channel(star.sed,
                                   planet.cr,
                                   zodi.sed,
                                   instrument_emission,
                                   instrument_transmission,
                                   options=ch)

        ch_optical_surface = ch.optical_surface if isinstance(ch.optical_surface, list) else \
          [ch.optical_surface]
        for op in ch_optical_surface:
            dtmp = np.loadtxt(op.transmission.replace('__path__',
                                                      opt.__path__),
                              delimiter=',')
            tr = Sed(dtmp[:,0]*pq.um, \
                    dtmp[:,1]*pq.dimensionless)
            tr.rebin(opt.common.common_wl)
            em = Sed(dtmp[:,0]*pq.um, \
                     dtmp[:,2]*pq.dimensionless)
            em.rebin(opt.common.common_wl)
            exolib.sed_propagation(channel[ch.name].star, tr)
            exolib.sed_propagation(channel[ch.name].zodi, tr)
            exolib.sed_propagation(channel[ch.name].emission, \
                    tr, emissivity=em,temperature=op())
            channel[ch.name].transmission.sed *= tr.sed

        # Convert spectral signals
        dtmp = np.loadtxt(ch.qe().replace('__path__', opt.__path__),
                          delimiter=',')
        qe = Sed(dtmp[:,0]*pq.um, \
                     dtmp[:,1]*pq.dimensionless)

        Responsivity = qe.sed * qe.wl.rescale(
            pq.m) / (spc.c * spc.h * pq.m * pq.J) * pq.UnitQuantity(
                'electron', symbol='e-')

        Re = scipy.interpolate.interp1d(qe.wl, Responsivity)

        Aeff = 0.25 * np.pi * opt.common_optics.TelescopeEffectiveDiameter()**2
        Omega_pix = 2.0 * np.pi * (1.0 -
                                   np.cos(np.arctan(0.5 / ch.wfno()))) * pq.sr
        Apix = ch.detector_pixel.pixel_size()**2
        channel[ch.name].star.sed     *= Aeff             * \
          Re(channel[ch.name].star.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
        channel[ch.name].zodi.sed     *= Apix * Omega_pix * \
          Re(channel[ch.name].zodi.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J
        channel[ch.name].emission.sed *= Apix * Omega_pix * \
          Re(channel[ch.name].emission.wl)*pq.UnitQuantity('electron', 1*pq.counts, symbol='e-')/pq.J

        ### create focal plane

        #1# allocate focal plane with pixel oversampling such that Nyquist sampling is done correctly
        fpn = ch.array_geometry()
        fp = np.zeros((fpn * ch.osf()).astype(np.int))

        #2# This is the current sampling interval in the focal plane.
        fp_delta = ch.detector_pixel.pixel_size() / ch.osf()

        #### NEW: for multiple orders. General idea here is to create a new focal
        #### plane for each order. I'll accomplish this by stacking them horizontally
        #### and then breaking them apart and adding them back together in the noise module
        #### after the light curve model is created and applied. Added by David Wright 09/20

        # check if we have multiple dispersion files
        if ch.type == 'spectrometer':
            if hasattr(ch, "dispersion"):
                ch_dispersion = ch.dispersion if isinstance(ch.dispersion, list) else \
                  [ch.dispersion]

                # allocate some arrays
                fp_orig = fp.copy()
                fp = np.zeros(
                    ((fpn[0] * ch.osf()).astype(np.int),
                     (fpn[1] * ch.osf() * len(ch_dispersion)).astype(np.int)))
                channel[ch.name].wl_solution = np.zeros(fp.shape[1])
                channel[ch.name].y_trace = np.zeros(
                    fp.shape[1])  # same size as above

                m_ord_thru = np.zeros(
                    fp.shape[1])  # throughput for different orders

                #mult_ord = (hasattr(ch, "dispersion") and (isinstance(ch.dispersion, list)))
                #if mult_ord:
                for i, disp in enumerate(ch_dispersion):
                    #for i,disp in enumerate(ch_dispersion[1:]):
                    dtmp = np.loadtxt(disp.path.replace(
                        '__path__', opt.__path__),
                                      delimiter=',')
                    wav = dtmp[..., 0]
                    # translate one "focal plane" over, we'll restack later
                    #x_shift = i * fp_delta * fp_orig.shape[1]
                    #pathx = dtmp[...,2]*pq.um + disp.val[0].rescale(pq.um) + x_shift
                    pathx = dtmp[..., 2] * pq.um + disp.val[0].rescale(pq.um)
                    pathy = dtmp[..., 3] * pq.um + disp.val[1].rescale(pq.um)
                    pathint = scipy.interpolate.interp1d(pathx,
                                                         pathy,
                                                         bounds_error=False,
                                                         fill_value=np.nan,
                                                         kind='linear')
                    ld = scipy.interpolate.interp1d(pathx,
                                                    wav,
                                                    bounds_error=False,
                                                    fill_value=0,
                                                    kind='linear')

                    # translate as before
                    #x_pix_osr = np.arange(fp_orig.shape[1]) * fp_delta + x_shift
                    x_pix_osr = np.arange(fp_orig.shape[1]) * fp_delta
                    y_pix_osr = pathint(x_pix_osr.rescale(pq.um)) * pq.um

                    x_wav_osr = ld(x_pix_osr.rescale(pq.um)) * pq.um

                    start = i * x_pix_osr.size
                    stop = min(start + x_pix_osr.size,
                               channel[ch.name].wl_solution.size)
                    channel[ch.name].wl_solution[start:stop] = x_wav_osr.copy()
                    channel[ch.name].y_trace[start:stop] = y_pix_osr.copy()

                    if (hasattr(ch.dispersion, "trpath")):
                        dtmp = np.loadtxt(disp.trpath.replace(
                            '__path__', opt.__path__),
                                          delimiter=',')
                        tr = Sed(dtmp[:,0]*pq.um, \
                                 dtmp[:,1]*pq.dimensionless)
                        tr.rebin(x_wav_osr)

                        m_ord_thru[start:stop] = tr.sed
                    else:
                        m_ord_thru[start:stop] = np.ones_like(
                            m_ord_thru[start:stop])

                # adding these so I don't have to rewrite much of the below code
                # Interesting behavior: can't *= with quantities
                channel[ch.name].y_trace = channel[ch.name].y_trace * pq.um
                channel[
                    ch.name].wl_solution = channel[ch.name].wl_solution * pq.um
                y_pix_osr = channel[ch.name].y_trace
                x_wav_osr = channel[ch.name].wl_solution

            elif hasattr(ch, "ld"):
                ld = np.poly1d(
                    (ch.ld()[1], ch.ld()[0] - ch.ld()[1] * ch.ld()[2]))
                x_pix_osr = np.arange(fp.shape[1]) * fp_delta
                # Need to be in center of detector. ExoSim no longer assumes this
                # after my changes.
                y_pix_osr = (np.zeros_like(x_pix_osr.magnitude) +
                             fp.shape[0] // 2) * fp_delta
                x_wav_osr = ld(x_pix_osr.rescale(pq.um)) * pq.um
                channel[ch.name].wl_solution = x_wav_osr
                m_ord_thru = np.ones(
                    fp.shape[1])  # throughput for different orders
            else:
                exolib.exosim_error("Dispersion law not defined.")

        elif ch.type == 'photometer':
            #4b# Estimate pixel and wavelength coordinates
            idx = np.where(channel[ch.name].transmission.sed >
                           channel[ch.name].transmission.sed.max() / np.e)
            x_wav_osr = np.linspace(
                channel[ch.name].transmission.wl[idx].min().item(),
                channel[ch.name].transmission.wl[idx].max().item(),
                8 * ch.osf()) * channel[ch.name].transmission.wl.units
            x_wav_center = (channel[ch.name].transmission.wl[idx]*channel[ch.name].transmission.sed[idx]).sum() / \
              channel[ch.name].transmission.sed[idx].sum()

            channel[ch.name].wl_solution = np.repeat(x_wav_center, fp.shape[1])

            # Need to be in center of detector. ExoSim no longer assumes this
            # after my changes.
            y_pix_osr = (np.zeros_like(x_pix_osr.magnitude) +
                         fp.shape[0] // 2) * fp_delta

            m_ord_thru = np.ones(
                fp.shape[1])  # throughput for different orders

        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

        d_x_wav_osr = np.zeros_like(x_wav_osr)
        idx = np.where(x_wav_osr > 0.0)

        # make sure we don't take the gradient between two orders
        # the original code would treat all nonzeros as if they were
        # next to each other, creating bright spots at the point where
        # the "fake" focal plane is joined
        if ch.type == 'spectrometer':
            if hasattr(ch, "dispersion"):
                idx = np.array(idx[0])  # fix weirdness with [0]
                num_disp = len(ch_dispersion)
                for i in np.arange(num_disp):
                    # get a split of idx that covers one "focal plane"
                    idx_split = idx[
                        np.where((idx < (i + 1) * x_wav_osr.size / num_disp)
                                 & (idx >= i * x_wav_osr.size / num_disp))]
                    d_x_wav_osr[idx_split] = np.gradient(x_wav_osr[idx_split])
            else:
                d_x_wav_osr[idx] = np.gradient(x_wav_osr[idx])

        else:
            d_x_wav_osr[idx] = np.gradient(x_wav_osr[idx])

        if np.any(d_x_wav_osr < 0): d_x_wav_osr *= -1.0

        if ((hasattr(channel[ch.name].opt, "webb_psf"))
                and (channel[ch.name].opt.webb_psf.use_webbpsf() == "True")):

            webb_psf_opt = channel[ch.name].opt.webb_psf
            print("\n Working in instrument.py \n")
            #print(ch.plate_scale())
            #scale = np.rad2deg(ch.plate_scale().item())
            ypix, xpix = exolib.get_webb_psf_dims(webb_psf_opt.psf_file(),
                                                  ch.osf().item())
            psf = np.zeros((ypix, xpix, x_wav_osr.size))
            for i, _ in enumerate(ch_dispersion):
                start = i * x_pix_osr.size
                stop = min(start + x_pix_osr.size,
                           channel[ch.name].wl_solution.size)
                print(start, stop)
                psf[...,start:stop] = exolib.webb_Psf_Interp(webb_psf_opt.psf_file(),ch.osf().item(),x_wav_osr[start:stop],\
                                           (y_pix_osr[start:stop].rescale(pq.um)/ch.detector_pixel.pixel_size().rescale(pq.um) * ch.osf().item()))
            #import matplotlib.pyplot as plt
            #middle = psf.shape[-1] // 2
            #plt.imshow(psf[...,middle])
            #plt.show()
            #sys.exit()

        #5# Generate PSFs, one in each detector pixel along spectral axis
        elif ((ch.type == 'spectrometer') and (hasattr(ch, "dispersion"))):
            psf = exolib.Psf(x_wav_osr, (y_pix_osr.rescale(pq.um)/ch.detector_pixel.pixel_size().rescale(pq.um) * ch.osf().item())\
                           , ch.wfno(), fp_delta, shape='airy')
        else:
            psf = exolib.Psf_orig(x_wav_osr, ch.wfno(), \
                                  fp_delta, shape='airy')

        # where psf is nans, replace with zeros
        nanidx = np.isnan(psf)
        psf[nanidx] = 0.0

        # Want to only fill up one oversampled pixel for testing
        # so that we don't spill into another pixel. Makes summing at a specific
        # wavelength less of a headache
        if (test_fp or test_multord):
            zidx = np.where(psf == 0.0)[-1]
            psf = np.ones((1, 1, psf.shape[-1]))
            psf[..., zidx] = 0.0

        #6# Save results in Channel class
        channel[ch.name].fp_delta = fp_delta
        channel[ch.name].psf = psf
        channel[ch.name].fp = fp
        channel[ch.name].osf = np.int(ch.osf())
        channel[ch.name].offs = np.int(ch.pix_offs())

        channel[ch.name].planet.sed *= channel[ch.name].star.sed
        channel[ch.name].star.rebin(x_wav_osr)
        channel[ch.name].planet.rebin(x_wav_osr)
        channel[ch.name].zodi.rebin(x_wav_osr)
        channel[ch.name].emission.rebin(x_wav_osr)
        channel[ch.name].transmission.rebin(x_wav_osr)
        channel[ch.name].star.sed *= d_x_wav_osr
        channel[ch.name].planet.sed *= d_x_wav_osr
        channel[ch.name].zodi.sed *= d_x_wav_osr
        channel[ch.name].emission.sed *= d_x_wav_osr

        # this block takes care of throughputs for each spectral order
        # m_ord_thru is all ones if single-ordered or photometric
        channel[ch.name].star.sed *= m_ord_thru
        channel[ch.name].planet.sed *= m_ord_thru
        channel[ch.name].zodi.sed *= m_ord_thru
        channel[ch.name].transmission.sed *= m_ord_thru
        channel[ch.name].emission.sed *= m_ord_thru

        if ch.type == 'spectrometer':
            j0 = np.round(np.arange(fp.shape[1]) - psf.shape[1] // 2).astype(
                np.int)

        elif ch.type == 'photometer':
            j0 = np.repeat(fp.shape[1] // 2, x_wav_osr.size)
        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

        j1 = j0 + psf.shape[1]

        # loop over fake focal planes here, making a separate idx for each one
        if ((ch.type == 'spectrometer') and hasattr(ch, "dispersion")):
            for i, _ in enumerate(ch_dispersion):
                start = i * x_pix_osr.size
                stop = (i + 1) * x_pix_osr.size
                idx = np.where((j0 >= start) & (j1 <= stop)
                               & (np.isfinite(y_pix_osr)))[0]

                for k in idx:
                    i0 = np.int((y_pix_osr[k].rescale(pq.um)/ch.detector_pixel.pixel_size().rescale(pq.um)\
                                 * ch.osf())//1 - psf.shape[0]//2 + channel[ch.name].offs)
                    i1 = np.int(i0 + psf.shape[0])
                    #cut = min(j1[k],stop)
                    psfcut = min(psf.shape[1], j1[k] - j0[k])
                    #if (j0[k] > cut): print('uh oh')
                    channel[ch.name].fp[i0:i1, j0[k]:j1[k]] += psf[:,:psfcut,k] * \
                      channel[ch.name].star.sed[k]

                #9# Now deal with the planet
                planet_response = np.zeros(fp.shape[1])
                i0p = np.unravel_index(
                    np.argmax(channel[ch.name].psf.sum(axis=2)),
                    channel[ch.name].psf[..., 0].shape)[0]
                for k in idx:
                    psfcut = min(psf.shape[1], j1[k] - j0[k])
                    planet_response[j0[k]:j1[k]] += psf[
                        i0p, :psfcut, k] * channel[ch.name].planet.sed[k]
        else:
            idx = np.where((j0 >= 0) & (j1 < fp.shape[1]))[0]
            i0 = fp.shape[0] // 2 - psf.shape[0] // 2 + channel[ch.name].offs
            i1 = i0 + psf.shape[0]
            for k in idx:
                channel[ch.name].fp[i0:i1, j0[k]:j1[k]] += psf[...,k] * \
                                            channel[ch.name].star.sed[k]

        #9# Now deal with the planet
        planet_response = np.zeros(fp.shape[1])
        i0p = np.unravel_index(np.argmax(channel[ch.name].psf.sum(axis=2)),
                               channel[ch.name].psf[..., 0].shape)[0]
        for k in idx:
            planet_response[j0[k]:j1[k]] += psf[i0p, :, k] * channel[
                ch.name].planet.sed[k]

        # For testing multiple orders.
        if (test_multord):
            import sys
            fdir = "tests/reference/"
            fname = "mord_test_order1.npy"
            fname_xwav = "xwav_order1.npy"
            np.save(fdir + fname, channel[ch.name].fp)
            np.save(fdir + fname_xwav, x_wav_osr)
            sys.exit("Testing multiple orders. Exiting early.")

        # For Testing. Save out the focal plane at this stage.
        # Meant to test with 55 Cancri black body out of transit
        # 0.5m telescope
        # R=254
        # fnum = 18.5
        if (test_fp):
            import sys
            fdir = "tests/reference/"
            fname_fp = "sim_image_mult-ord.npy"
            fname_xwav = "xwav_mult-ord.npy"
            fname_exact = "exact_image_mult-ord.npy"
            np.save(fdir + fname_xwav, x_wav_osr)
            star_temp = 5172 * pq.K
            #star_radius = (0.943 * 6.957e5 * pq.km).rescale(pq.m)
            star_radius = 659628500.0 * pq.m
            print(star_radius)
            #star_distance = (12.5901 * pq.parsec).rescale(pq.m)
            star_distance = 3.8849022915525e+17 * pq.m
            print(star_distance)
            omega_star = np.pi * (star_radius / star_distance)**2 * pq.sr
            print("omega_star_instrument.py", omega_star)
            print(omega_star)
            area_tel = np.pi * 0.25 * (0.5 * pq.m)**2
            print(area_tel)
            channel[ch.name].transmission.rebin(x_wav_osr)
            qe.rebin(x_wav_osr)
            #step_avg = (x_wav_osr.max() - x_wav_osr.min()) / len(x_wav_osr)
            step = np.diff(x_wav_osr)
            # need 1 more step
            step = np.append(step, step[-3:].mean()) * pq.um

            exact = omega_star * exolib.planck(x_wav_osr,star_temp) * \
              area_tel * channel[ch.name].transmission.sed * \
              qe.sed * step * x_wav_osr / (pq.c * spc.h * pq.J * pq.s)
            exact = exact.simplified
            print(qe.sed)
            print(channel[ch.name].transmission.sed)
            print(exolib.planck(x_wav_osr, star_temp))
            print(exact)
            sim = channel[ch.name].fp.sum(axis=0)
            print(sim)
            print(x_wav_osr)
            np.save(fdir + fname_exact, exact)
            np.save(fdir + fname_fp, sim)
            sys.exit("Focal plane testing run, ending early.")

        #9# Allocate pixel response function
        kernel, kernel_delta = exolib.PixelResponseFunction(
            channel[ch.name].psf.shape[0:2],
            7 * ch.osf(),  # NEED TO CHANGE FACTOR OF 7 
            ch.detector_pixel.pixel_size(),
            lx=ch.detector_pixel.pixel_diffusion_length())

        if ((ch.type == 'spectrometer') and hasattr(ch, "dispersion")):
            for i, _ in enumerate(ch_dispersion):
                start = i * x_pix_osr.size
                stop = (i + 1) * x_pix_osr.size
                channel[ch.name].fp[:, start:stop] = exolib.fast_convolution(
                    channel[ch.name].fp[:, start:stop],
                    channel[ch.name].fp_delta, kernel, kernel_delta)
        else:
            channel[ch.name].fp = exolib.fast_convolution(
                channel[ch.name].fp, channel[ch.name].fp_delta, kernel,
                kernel_delta)


## TODO CHANGE THIS: need to convolve planet with pixel response function
        if ((ch.type == 'spectrometer') and hasattr(ch, "dispersion")):
            response_temp = np.zeros(fp.shape[1])
            for i, k in enumerate(idx):
                i0 = np.int((y_pix_osr[k].rescale(pq.um)/ch.detector_pixel.pixel_size().rescale(pq.um) \
                             * ch.osf().item())//1 - psf.shape[0]//2 + channel[ch.name].offs)
                i1 = np.int(i0 + psf.shape[0])
                response_temp[k] = planet_response[k] / (1e-30 +
                                                         fp[(i0 + i1) // 2, k])

            channel[ch.name].planet = Sed(channel[ch.name].wl_solution,
                                          response_temp)
        else:
            channel[ch.name].planet = Sed(
                channel[ch.name].wl_solution,
                planet_response / (1e-30 + fp[(i0 + i1) // 2, ...]))

        ## Fix units
        channel[
            ch.name].fp = channel[ch.name].fp * channel[ch.name].star.sed.units
        channel[ch.name].planet.sed = channel[
            ch.name].planet.sed * pq.dimensionless

        ## Deal with diffuse radiation
        if ch.type == 'spectrometer':
            channel[ch.name].zodi.sed = scipy.convolve(
                channel[ch.name].zodi.sed,
                np.ones(np.int(ch.slit_width() * channel[ch.name].opt.osf())),
                'same') * channel[ch.name].zodi.sed.units
            channel[ch.name].emission.sed = scipy.convolve(
                channel[ch.name].emission.sed,
                np.ones(np.int(ch.slit_width() * channel[ch.name].opt.osf())),
                'same') * channel[ch.name].emission.sed.units
        elif ch.type == 'photometer':
            channel[ch.name].zodi.sed = np.repeat(
                channel[ch.name].zodi.sed.sum(),
                channel[ch.name].wl_solution.size)
            channel[ch.name].zodi.wl = channel[ch.name].wl_solution
            channel[ch.name].emission.sed = np.repeat(
                channel[ch.name].emission.sed.sum(),
                channel[ch.name].wl_solution.size)
            channel[ch.name].emission.wl = channel[ch.name].wl_solution

        else:
            exolib.exosim_error(
                "Channel should be either photometer or spectrometer.")

    exosim_msg(' - execution time: {:.0f} msec.\n'.format(
        (time.time() - st) * 1000.0))
    return channel

    pass
Beispiel #16
0
def run(opt, channel, planet):
  exosim_msg('Save to file ... ')
  st = time.time()
  out_path = os.path.expanduser(
    opt.common.ExoSimOutputPath().replace('__path__', opt.__path__))
  
  if not os.path.exists(out_path):
    os.mkdir(out_path)
  
  existing_sims = glob.glob(os.path.join(out_path, 'sim_*'))
  
  if not existing_sims:
    # Empty list
    sim_number = 0
  else:
    sim_number = sorted([np.int(l.split('_')[-1]) for l in existing_sims])[-1]
    sim_number += 1
  
  out_path = os.path.join(out_path, 'sim_{:04d}'.format(sim_number))
  os.mkdir(out_path)
  
  
    
  for key in channel.keys():
    n_ndr      = channel[key].tl_shape[-1]
    multiaccum = np.int(opt.timeline.multiaccum())
    n_exp      = n_ndr // multiaccum
    hdu        = fits.PrimaryHDU()
    hdu.header['NEXP'] = (n_exp, 'Number of exposures')
    hdu.header['MACCUM'] = (multiaccum, 'Multiaccum')
    hdu.header['TEXP'] = (channel[key].exposure_time.item(), 'Exp Time [s]')
    hdu.header['PLANET'] = (planet.planet.name, 'Planet name')
    hdu.header['STAR'] = (planet.planet.star.name, 'Star name')
    
    #### Detector Simulation Values
    hdu.header['POINTRMS'] = (opt.aocs.pointing_rms.val.item(), 'Model Pointing RMS')
    tempVal = filter(lambda x:x.name==key, opt.channel)[0].detector_pixel.Idc.val.magnitude.item()
    hdu.header['DET_I_DC'] = (tempVal, 'Detector dark current')
    tempVal = filter(lambda x:x.name==key, opt.channel)[0].detector_pixel.sigma_ro.val.magnitude.item()
    hdu.header['DETROERR'] = (tempVal, 'Detector readout noise in e-rms')
    tempVal = filter(lambda x:x.name==key, opt.channel)[0].plate_scale.val.rescale('degree').magnitude.item()
    hdu.header['CDELT1'] = (tempVal, 'Degrees/pixel')
    hdu.header['CDELT2'] = (tempVal, 'Degrees/pixel')
    hdulist = fits.HDUList(hdu)
    
    
    
    
    for i in xrange(channel[key].tl_shape[-1]):
      hdu = fits.ImageHDU(channel[key].noise[..., i].astype(np.float32), name = 'NOISE')
      hdu.header['EXPNUM'] = (i//multiaccum, 'Exposure Number')
      hdu.header['ENDRNUM'] = (i%multiaccum, 'NDR Number')
      hdu.header['EXTNAME'] = 'NOISE'   
      hdulist.append(hdu)
    
    		     
    # Create column data
    col1 = fits.Column(name='Wavelength {:s}'.format(channel[key].wl_solution.units), format='E', 
		       array=channel[key].wl_solution[channel[key].offs::channel[key].osf])
    col2 = fits.Column(name='Input Contrast Ratio', format='E', 
		       array=channel[key].planet.sed[channel[key].offs::channel[key].osf])
    col3 = fits.Column(name='Stellar SED', format='E',
                       array=channel[key].star.sed[channel[key].offs::channel[key].osf])
    cols = fits.ColDefs([col1, col2, col3])
    tbhdu = fits.BinTableHDU.from_columns(cols)
    tbhdu.name = 'INPUTS'
    hdulist.append(tbhdu)
    ########
    hdu = fits.ImageHDU(channel[key].outputPointingTl, name = 'SIM_POINTING')
    hdulist.append(hdu)
    
    ############
    col1 = fits.Column(name='Time {:s}'.format(channel[key].ndr_time.units), format='E', 
		       array=channel[key].ndr_time)
    col2 = fits.Column(name='z', format='E', 
		       array=channel[key].z)
    
    cols = fits.ColDefs([col1, col2])
    tbhdu = fits.BinTableHDU.from_columns(cols)
    tbhdu.name = 'TIME'
    hdulist.append(tbhdu)
    #########
    
    hdu = fits.ImageHDU(channel[key].lc, name = 'LIGHT_CURVES')
    hdulist.append(hdu)
    
    #print hdulist
    hdulist.writeto(os.path.join(out_path, '{:s}_signal.fits'.format(key)))
  
  exosim_msg(' - execution time: {:.0f} msec.\n'.format((time.time()-st)*1000.0))