def make_mult_pseudo_obs(offsets=0., slitamps=1., nfib=19, simpath='/Volumes/BERGRAID/data/simu/', outpath='/Volumes/BERGRAID/data/simu/composite/', norm_slitamps=False, template=False, add_laser=False, savefile=True, verbose=False, debug_level=0, timit=False): """ This routine creates simulated observations by adding together the individual-fibre spectra from my EchelleSimulator library using the appropriate relative intensities. It does essentially the same as "make_pseudo_obs", but takes fibre-offsets and slitamps as user-defined inputs (ie as if 'fixed_offsets' and 'fixed_slitamps' were both set to TRUE in "make_pseudo_obs", and no 'seeing' array was provided). INPUT: 'offsets' : constant offset for all fibres (can be scalar (all fibres have same offset), or array of size (nobs,nfib)) 'slitamps' : constant slitamps for all fibres (=relative intensities) (can be scalar (all fibres have same offset), or array of size (nobs,nfib)) 'nfib' : the number of (stellar) fibres to use 'simpath' : path to my EchelleSimulator spectrum library 'outpath' : root-directory for the output files (sub-directories will be created every time this routine is run because of the random-normal pseudo-slit alignments) 'norm_slitamps' : boolean - do you want to 'normalize' the slitamps so they add up to 1? (only used if 'fixed_slitamps' is set to TRUE) 'template' : boolean - is this going to be a template? in that case use the 'maxsnr' spectra... 'add_laser' : boolean - do you want to add the LFC in fibre 1? 'savefile' : boolean - do you want to save the simulated spectra as FITS files? 'verbose' : boolean - for debugging... 'debug_level' : boolean - for debugging... 'timit' : boolean - do you want to clock the run-time? OUTPUT: 'master_dict' : dictionary containing the simulated spectra for all seeing values (only if the 'savefile' keyword is not set, otherwise results are saved to FITS file(s)) TODO: add SNR - for now this is for a given (fixed) observing time, i.e. the total flux captured by the IFU drops as the seeing increases MODHIST: 02/07/2018 - CMB create (clone of "make_pseudo_obs") """ if timit: start_time = time.time() ##### (1) ##### # fibre-offsets formatting # (i) if a scalar is provided; nobs=1, use for all fibres if len(get_iterable(offsets)) == 1: nobs_from_offsets = 1 offsets = np.repeat(offsets, nfib) #how can you do this for multiple obs? if debug_level >= 1: plot_pseudoslit(offsets) # (ii) if a nfib-element array is provided; nobs=1 elif np.array(offsets).ndim == 1: if offsets.shape[0] == nfib: nobs_from_offsets = 1 offsets = np.array(offsets) else: print('ERROR: "offsets" has the wrong shape!!!') return #how can you do this for multiple obs? if debug_level >= 1: plot_pseudoslit(offsets) # (iii) a (nx,nfib)-element array is provided; nobs=nx else: if offsets.shape[1] != nfib: print('ERROR: "offsets" has the wrong shape!!!') return else: nobs_from_offsets = offsets.shape[0] ##### (2) ##### # slitamps formatting # (i) if a scalar is provided; nobs=1, use for all fibres if len(get_iterable(slitamps)) == 1: nobs_from_slitamps = 1 slitamps = np.repeat(slitamps, nfib).astype(float) # (ii) if a nfib-element array is provided; nobs=1 elif np.array(slitamps).ndim == 1: if slitamps.shape[0] == nfib: nobs_from_slitamps = 1 else: print('ERROR: "slitamps" has the wrong shape!!!') return # (iii) a (nx,nfib)-element array is provided; nobs=nx else: if slitamps.shape[1] != nfib: print('ERROR: "slitamps" has the wrong shape!!!') return else: nobs_from_slitamps = slitamps.shape[0] #make sure we have consistent dimensions between "offsets" and "slitamps" if nobs_from_offsets > nobs_from_slitamps: if nobs_from_slitamps == 1: print( 'WARNING: number of fibres inferred from "offsets" is LARGER than from "slitamps" !!!' ) print('Using same slitamps for all observations...') nobs = nobs_from_offsets slitamps_1dim = slitamps.copy() while nobs_from_slitamps < nobs_from_offsets: slitamps = np.vstack((slitamps, slitamps)) nobs_from_slitamps += 1 else: print( 'ERROR: dimensions of "offsets" and "slitamps" do not agree!!!' ) return elif nobs_from_offsets < nobs_from_slitamps: if nobs_from_offsets == 1: print( 'WARNING: number of fibres inferred from "offsets" is SMALLER than from "slitamps" !!!' ) print('Using same offsets for all observations...') nobs = nobs_from_slitamps offsets_1dim = offsets.copy() while nobs_from_offsets < nobs_from_slitamps: offsets = np.vstack((offsets, offsets_1dim)) nobs_from_offsets += 1 else: print( 'ERROR: dimensions of "offsets" and "slitamps" do not agree!!!' ) return else: nobs = nobs_from_offsets ## reshape to accommodate the possibility of having multiple seeing conditions below # slitamps = np.reshape(slitamps, (19,1)) if norm_slitamps: slitsums = slitamps.sum(axis=1, keepdims=True) slitamps = slitamps.astype(float) / slitsums if savefile: #create new sub-folder with info files containing info on the fibre offsets and the slitamps (=relative intensities) datestring = get_datestring() dum = 1 newpath = outpath + 'tests_' + datestring dumpath = outpath + 'tests_' + datestring while os.path.exists(dumpath): dum += 1 dumpath = newpath + '_' + str(dum) if dum > 1: newpath = newpath + '_' + str(dum) #create new folder os.makedirs(newpath) else: master_dict = {} #loop over all observations for i in np.arange(nobs): if verbose: print('Simulating observation ' + str(i + 1) + '/' + str(nobs)) #some string manipulations for filenames in for loop below strshifts = np.abs(offsets[i, :]).astype(int).astype(str) redblue = np.empty(nfib).astype(str) redblue[offsets[i, :] > 0] = 'red' redblue[offsets[i, :] < 0] = 'blue' redblue[offsets[i, :] == 0] = '' if add_laser: laserstr = '_laser' else: laserstr = '' if template: tstring = '_template' maxsnr_string = '_maxsnr' else: tstring = '' maxsnr_string = '' #write OFFSETS file outfn = newpath + '/' + 'offsets_' + str(i + 1).zfill(len( str(nobs))) + '.txt' outfile = open(outfn, 'w') outfile.writelines( ["%s\n" % item for item in offsets[i, :].astype(str)]) outfile.close() #write SLITAMPS file outfn = newpath + '/' + 'slitamps_' + str(i + 1).zfill(len( str(nobs))) + '.txt' outfile = open(outfn, 'w') outfile.writelines( ["%s\n" % item for item in slitamps[i, :].astype(str)]) outfile.close() #use fibre-slots 6 to 24 for n in range(nfib): fibslot = str(n + 6).zfill(2) img = pyfits.getdata(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') if n == 0: master = (img.copy().astype(float) * slitamps[i, n]) + 1. h = pyfits.getheader(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') else: master += img * slitamps[i, n] if add_laser: laser_img = pyfits.getdata(simpath + 'veloce_laser_comb.fit') master += laser_img / 5. if savefile: #save to file pyfits.writeto(newpath + '/syntobs_' + str(i + 1).zfill(len(str(nobs))) + laserstr + tstring + '.fit', master, h, clobber=True) else: master_dict['syntobs_' + str(i + 1)] = master if timit: print('Time elapsed: ' + str(np.round(time.time() - start_time, 1)) + ' seconds') if savefile: #print('Offsets: ',offsets) return else: return master_dict
def make_pseudo_obs(seeing=None, simpath='/Volumes/BERGRAID/data/simu/', nfib=19, outpath='/Volumes/BERGRAID/data/simu/composite/', fixed_offsets=False, fixoff=0., fixed_slitamps=False, slitamps=1., norm_slitamps=False, template=False, add_laser=False, savefile=True, verbose=False, debug_level=0, timit=False): """ This routine creates simulated observations by (1) calculating the flux-ratios / relative intensities in the individual fibres, (2) simulating a pseudo-slit with a random normal distribution of relative offsets of the fibres relative to the centre (ie simulating alignment imperfections), (3) adding together the individual-fibre spectra from my EchelleSimulator library using the appropriate relative intensities. INPUT: 'seeing' : FWHM values of desired simulated seeing condition(s) - can be scalar or array 'simpath' : path to my EchelleSimulator spectrum library 'nfib' : the number of (stellar) fibres to use 'outpath' : root-directory for the output files (sub-directories will be created every time this routine is run because of the random-normal pseudo-slit alignments) 'fixed_offsets' : boolean - do you want to simulate fibre-offsets in dispersion direction for the pseudoslit? (set to TRUE if you don't want to simulate offsets, but rather use a constant offset 'fixoff' for all fibres) 'fixoff' : constant offset for all fibres (only used if 'fixed_offsets' is set to TRUE) 'fixed_slitamps' : boolean - do you want to simulate the effect of seeing or provide fixed relative intensities? (if TRUE, the relative intensities are taken from 'slitamps'; if set to FALSE the relative intensities are calculated using the provided seeing values) 'slitamps' : constant slitamps for all fibres (=relative intensities) (only used if 'fixed_slitamps' is set to TRUE) 'norm_slitamps' : boolean - do you want to 'normalize' the slitamps so they add up to 1? (only used if 'fixed_slitamps' is set to TRUE) 'template' : boolean - is this going to be a template? in that case use the 'maxsnr' spectra... 'add_laser' : boolean - do you want to add the LFC in fibre 1? 'savefile' : boolean - do you want to save the simulated spectra as FITS files? 'verbose' : boolean - for debugging... 'debug_level' : boolean - for debugging... 'timit' : boolean - do you want to clock the run-time? OUTPUT: 'master_dict' : dictionary containing the simulated spectra for all seeing values (only if the 'savefile' keyword is not set, otherwise results are saved to FITS file(s)) TODO: add SNR - for now this is for a given (fixed) observing time, i.e. the total flux captured by the IFU drops as the seeing increases MODHIST: 16/05/2018 - CMB create 08/06/2018 - CMB added 'add_laser' keyword; also changed the addition of 1 to the first image only 13/06/2018 - CMB added 'template' keyword (uses the 'maxsnr' simulated spectra) 14/06/2018 - CMB added "slitamps.txt" output file; also added 'fixed_slitamps', 'slitamps', and 'norm_slitamps' keywords """ if timit: start_time = time.time() ##### (1) ##### if not fixed_slitamps: if seeing is not None: #first, calculate flux_ratios (ie relative intensities) for the different fibres from given seeing fr = flux_ratios_from_seeing(seeing, verbose=verbose) #then get an array of the relative intensities in the pseudo-slit slitamps = get_pseudo_slitamps(fr['central'], fr['inner'], fr['outer1'], fr['outer2']) else: print( 'ERROR: "fixed_slitamps" = False, but seeing not provided!!!') return else: if len(get_iterable(slitamps)) == 1: slitamps = np.repeat(slitamps, nfib).astype(float) # #reshape to accommodate the possibility of having multiple seeing conditions below # slitamps = np.reshape(slitamps, (19,1)) if norm_slitamps: slitamps = slitamps.astype(float) / np.sum(slitamps) ##### (2) ##### #simulate fibre-offsets by using RV offsets if not fixed_offsets: offsets = make_pseudoslits() else: if len(get_iterable(fixoff)) == 1: offsets = np.repeat(fixoff, nfib) elif len(get_iterable(fixoff)) == nfib: offsets = np.array(fixoff) else: print( 'ERROR! input values for fibre offsets do not have correct length' ) return if debug_level >= 1: plot_pseudoslit(offsets) ##### (3) ##### #some string manipulations for filenames in for loop below strshifts = np.abs(offsets).astype(int).astype(str) redblue = np.empty(nfib).astype(str) redblue[offsets > 0] = 'red' redblue[offsets < 0] = 'blue' redblue[offsets == 0] = '' if add_laser: laserstr = '_laser' else: laserstr = '' if template: tstring = '_template' maxsnr_string = '_maxsnr' else: tstring = '' maxsnr_string = '' if savefile: #create new sub-folder with info files containing info on the fibre offsets and the slitamps (=relative intensities) datestring = get_datestring() dum = 1 newpath = outpath + 'tests_' + datestring dumpath = outpath + 'tests_' + datestring while os.path.exists(dumpath): dum += 1 dumpath = newpath + '_' + str(dum) if dum > 1: newpath = newpath + '_' + str(dum) #create new folder os.makedirs(newpath) #write OFFSETS file outfn = newpath + '/' + 'offsets.txt' outfile = open(outfn, 'w') outfile.writelines(["%s\n" % item for item in offsets.astype(str)]) outfile.close() #write SLITAMPS file outfn = newpath + '/' + 'slitamps.txt' outfile = open(outfn, 'w') outfile.writelines(["%s\n" % item for item in slitamps.astype(str)]) outfile.close() else: master_dict = {} if seeing is not None: for i, fwhm in enumerate(get_iterable(seeing)): if verbose: print('SEEING-LOOP: ', fwhm) #use fibre-slots 6 to 24 for n in range(nfib): fibslot = str(n + 6).zfill(2) img = pyfits.getdata(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') if n == 0: master = (img.copy().astype(float) * slitamps[n, i]) + 1. h = pyfits.getheader(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') else: master += img * slitamps[n, i] if add_laser: laser_img = pyfits.getdata(simpath + 'veloce_laser_comb.fit') master += laser_img / 5. if savefile: #save to file pyfits.writeto(newpath + '/seeing' + fwhm.astype(str) + laserstr + tstring + '.fit', master, h, clobber=True) else: master_dict['seeing' + fwhm.astype(str)] = master else: for n in range(nfib): fibslot = str(n + 6).zfill(2) img = pyfits.getdata(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') if n == 0: master = (img.copy().astype(float) * slitamps[n]) + 1. h = pyfits.getheader(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms' + maxsnr_string + '.fit') else: master += img * slitamps[n] if add_laser: laser_img = pyfits.getdata(simpath + 'veloce_laser_comb.fit') master += laser_img / 5. if savefile: #save to file pyfits.writeto(newpath + '/syntobs' + laserstr + tstring + '.fit', master, h, clobber=True) else: master_dict['syntobs' + tstring] = master if timit: print('Time elapsed: ' + str(np.round(time.time() - start_time, 1)) + ' seconds') if savefile: #print('Offsets: ',offsets) return else: return master_dict
def make_pseudo_obs_with_noise( seeing, snrs=None, RON=0., simpath='/Volumes/BERGRAID/data/simu/', nfib=19, outpath='/Volumes/BERGRAID/data/simu/composite/', fixed_offsets=False, fixoff=0., fixed_slitamps=False, slitamps=1., norm_slitamps=False, template=False, add_laser=False, savefile=True, verbose=False, debug_level=0, timit=False): """ This routine creates simulated observations by (1) calculating the flux-ratios / relative intensities in the individual fibres, (2) simulating a pseudo-slit with a random normal distribution of relative offsets of the fibres relative to the centre (ie simulating alignment imperfections), (3) adding together the individual-fibre spectra from my EchelleSimulator library using the appropriate relative intensities. INPUT: 'seeing' : FWHM values of desired simulated seeing condition(s) - can be scalar or array 'snrs' : desired mean signal-to-noise ratio per extracted 1-dim pixel (WARNING: the seeing parameter does not renormalize to the flux captured by the IFU!!!) floats can be provided for 'snrs', but it will be rounded to integer 'RON' : read-out noise 'simpath' : path to my EchelleSimulator spectrum library 'nfib' : the number of (stellar) fibres to use 'outpath' : root-directory for the output files (sub-directories will be created every time this routine is run because of the random-normal pseudo-slit alignments) 'fixed_offsets' : boolean - do you want to simulate fibre-offsets in dispersion direction for the pseudoslit? (set to TRUE if you don't want to simulate offsets, but rather use a constant offset 'fixoff' for all fibres) 'fixoff' : constant offset for all fibres (only used if 'fixed_offsets' is set to TRUE) 'fixed_slitamps' : boolean - do you want to simulate the effect of seeing or provide fixed relative intensities? (if TRUE, the relative intensities are taken from 'slitamps'; if set to FALSE the relative intensities are calculated using the provided seeing values) 'slitamps' : constant slitamps for all fibres (=relative intensities) (only used if 'fixed_slitamps' is set to TRUE) 'norm_slitamps' : boolean - do you want to 'normalize' the slitamps so they add up to 1? (only used if 'fixed_slitamps' is set to TRUE) 'template' : boolean - is that going to be a template? in that case no noise is added... 'add_laser' : boolean - do you want to add the LFC in fibre 1? 'savefile' : boolean - do you want to save the simulated spectra as FITS files? 'verbose' : boolean - for debugging... 'debug_level' : boolean - for debugging... 'timit' : boolean - do you want to time the execution time? OUTPUT: 'master_dict' : dictionary containing the simulated spectra for all seeing values (only if the 'savefile' keyword is not set, otherwise results are saved to FITS file(s)) TODO: add SNR - for now this is for a given (fixed) observing time, i.e. the total flux captured by the IFU drops as the seeing increases MODHIST: 07/06/2018 - CMB create (clone of "make_pseudo_obs") 08/06/2018 - CMB added 'add_laser' keyword; also changed the addition of 1 to the first image only 14/06/2018 - CMB added "slitamps.txt" output file; also added 'fixed_slitamps', 'slitamps', and 'norm_slitamps' keywords """ if timit: start_time = time.time() ##### (1) ##### if not fixed_slitamps: if seeing is not None: #first, calculate flux_ratios (ie relative intensities) for the different fibres from given seeing fr = flux_ratios_from_seeing(seeing, verbose=verbose) #then get an array of the relative intensities in the pseudo-slit slitamps = get_pseudo_slitamps(fr['central'], fr['inner'], fr['outer1'], fr['outer2']) else: print( 'ERROR: "fixed_slitamps" = False, but seeing not provided!!!') return else: if len(get_iterable(slitamps)) == 1: slitamps = np.repeat(slitamps, nfib) #reshape to accommodate the possibility of having multiple seeing condittions below slitamps = np.reshape(slitamps, (19, 1)) if norm_slitamps: slitamps = slitamps / np.sum(slitamps) ##### (2) ##### #simulate fibre-offsets by using RV offsets if not fixed_offsets: #offsets = make_pseudoslits() print('ERROR: offsets in pixel space have not been implemented yet!!!') return else: if len(get_iterable(fixoff)) == 1: offsets = np.repeat(fixoff, nfib) elif len(get_iterable(fixoff)) == nfib: offsets = np.array(fixoff) else: print( 'ERROR! input values for fibre offsets do not have correct length' ) return if not (offsets == 0).all(): print('ERROR: offsets in pixel space have not been implemented yet!!!') return if debug_level >= 1: plot_pseudoslit(offsets) ##### (3) ##### #some string manipulations for filenames in for loop below strshifts = np.abs(offsets).astype(int).astype(str) redblue = np.empty(nfib).astype(str) redblue[offsets > 0] = 'red' redblue[offsets < 0] = 'blue' redblue[offsets == 0] = '' if add_laser: laserstr = '_laser' else: laserstr = '' if savefile: #create new sub-folder with README file containing info on the fibre offsets datestring = get_datestring() dum = 1 newpath = outpath + 'tests_' + datestring dumpath = outpath + 'tests_' + datestring while os.path.exists(dumpath): dum += 1 dumpath = newpath + '_' + str(dum) if dum > 1: newpath = newpath + '_' + str(dum) #create new folder os.makedirs(newpath) #write README file outfn = newpath + '/' + 'offsets.txt' outfile = open(outfn, 'w') outfile.writelines(["%s\n" % item for item in offsets.astype(str)]) outfile.close() #write SLITAMPS file outfn = newpath + '/' + 'slitamps.txt' outfile = open(outfn, 'w') outfile.writelines(["%s\n" % item for item in slitamps.astype(str)]) outfile.close() else: master_dict = {} for i, (fwhm, snr) in enumerate(zip(get_iterable(seeing), get_iterable(snrs))): snr = int(np.round(snr)) if verbose: print('SEEING: ', fwhm) print('SNR: ', snr) #use fibre-slots 6 to 24 for n in range(nfib): fibslot = str(n + 6).zfill(2) img = pyfits.getdata(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms_maxsnr.fit') + 1. if n == 0: master = img.copy().astype(float) * slitamps[n, i] h = pyfits.getheader(simpath + 'fib' + fibslot + '_' + redblue[n] + strshifts[n] + 'ms_maxsnr.fit') else: master += img * slitamps[n, i] #now add noise (a "maxsnr" spectrum has a mean SNR of ~555) if not template: fudge = (snr / 555.)**2. scaled_master = master * fudge err_amps = np.sqrt(scaled_master + RON * RON) #add noise to the image scaled_noise = make_scaled_white_noise(err_amps) noisy_img = scaled_master + scaled_noise else: #don't want to add noise for the templates noisy_img = master.copy() #remove any negative pixels noisy_img[noisy_img < 0] = 0. if add_laser: laser_img = pyfits.getdata(simpath + 'veloce_laser_comb.fit') noisy_img += laser_img / 5. if savefile: #save to file pyfits.writeto(newpath + '/seeing' + fwhm.astype(str) + '_snr_' + str(snr) + laserstr + '.fit', noisy_img, h, clobber=True) else: master_dict['seeing' + fwhm.astype(str)] = master if timit: print('Time elapsed: ' + str(np.round(time.time() - start_time, 1)) + ' seconds') if savefile: #print('Offsets: ',offsets) return else: return master_dict
def flux_ratios_from_seeing(seeing, verbose=False): ''' This routine calculates, as a function of seeing (which is approximated by a 2D Gaussian with FWHM = seeing in both directions), the flux ratios that the Veloce IFU captures in total, as well as the ratios of the flux captured by the central fibre / inner-ring fibres / outer-ring fibres to the total flux captured by the IFU (NOT the TOTAL total flux!!!) INPUT: 'seeing' : can be scalar or array/list; the FWHM of the seeing disk (approximated as a 2-dim Gaussian) KEYWORDS: 'verbose' : boolean - do you want verbose output printed to screen? OUTPUT: 'flux_ratios' : a python dictionary containing the results MODHIST: Dec 2017 - CMB create 11/05/2018 - CMB modified output format and included possibility to iterate over 1-element seeing-array ''' #initialise output dictionary flux_ratios = {} #inner-radius r of hexagonal fibre is 0.26", therefore outer-radius R is (2/sqrt(3))*0.26" = 0.30" #what we really want is the "effective" radius though, for a regular hexagon that comes from A = 3*r*R = 2*sqrt(3)*r = pi * r_eff**2 #ie r_eff = sqrt( (2*sqrt(3)) / pi ) # fac = np.sqrt((2.*np.sqrt(3.)) / np.pi) rc = 0.26 Rc = rc * 2. / np.sqrt(3.) # ri = 0.78 # ro = 1.30 # reff = rc * fac di = 0.52 do1 = 1.04 xo2 = di + rc yo2 = 1.5 * Rc # do2 = 3. * (2./np.sqrt(3.)) * 0.26 x = np.arange(-10, 10.01, .01) y = np.arange(-10, 10.01, .01) xx, yy = np.meshgrid(x, y) #define constant (FWHM vs sigma, because FWHM = sigma * 2*sqrt(2*log(2)) cons = 2 * np.sqrt(np.log(2)) m1 = 1. / np.sqrt(3.) #slope1 m2 = -m1 #slope2 central = np.logical_and( np.logical_and( np.logical_and( np.logical_and(np.abs(xx) <= rc, yy <= m1 * xx + Rc), yy >= m1 * xx - Rc), yy <= m2 * xx + Rc), yy >= m2 * xx - Rc) inner = np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.abs(xx - di) <= rc, yy <= m1 * (xx - di) + Rc), yy >= m1 * (xx - di) - Rc), yy <= m2 * (xx - di) + Rc), yy >= m2 * (xx - di) - Rc), ~central) outer1 = np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.abs(xx - do1) <= rc, yy <= m1 * (xx - do1) + Rc), yy >= m1 * (xx - do1) - Rc), yy <= m2 * (xx - do1) + Rc), yy >= m2 * (xx - do1) - Rc), ~inner) outer2 = np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.logical_and( np.abs(xx - xo2) <= rc, yy <= m1 * (xx - xo2) + Rc - yo2), yy >= m1 * (xx - xo2) - Rc - yo2), yy <= m2 * (xx - xo2) + Rc - yo2), yy >= m2 * (xx - xo2) - Rc - yo2), ~inner), ~outer1) ######################################################### # this was the circular approximation: # central = (np.sqrt(xx*xx + yy*yy) <= rc) # inner = np.logical_and(np.sqrt(xx*xx + yy*yy) <= ri, ~central) # outer = np.logical_and(np.sqrt(xx*xx + yy*yy) <= ro, np.logical_and(~central, ~inner)) # ifu = (np.sqrt(xx*xx + yy*yy) <= ro) # central = (np.sqrt(xx*xx + yy*yy) <= reff) # inner = np.sqrt((xx-di)*(xx-di) + yy*yy) <= reff # outer1 = np.sqrt((xx-do1)*(xx-do1) + yy*yy) <= reff # outer2 = np.sqrt((xx-do2)*(xx-do2) + yy*yy) <= reff # ifu = (np.sqrt(xx*xx + yy*yy) <= ro*fac) ######################################################### # if len(np.atleast_1d(seeing)) > 1: # # frac_ifu = [] # renorm_frac_c = [] # renorm_frac_i = [] # renorm_frac_o = [] # # for fwhm in seeing: # # if verbose: # print('Simulating '+str(fwhm)+'" seeing...') # # #calculate 2-dim Gaussian flux distribution as function of input FWHM # fx = np.exp(-(np.absolute(xx) * cons / fwhm) ** 2.0) # fy = np.exp(-(np.absolute(yy) * cons / fwhm) ** 2.0) # f = fx * fy * 1e6 # # frac_c = np.sum(f[central]) / np.sum(f) # frac_i = 6. * np.sum(f[inner]) / np.sum(f) # frac_o = 6. * ( np.sum(f[outer1]) + np.sum(f[outer2]) ) / np.sum(f) # ifu_frac = frac_c + frac_i + frac_o #this is slightly overestimated (<1%) because they are not actually circular fibres # frac_ifu.append(ifu_frac) # # rfc = frac_c / ifu_frac # rfi = frac_i / ifu_frac # rfo = frac_o / ifu_frac # # renorm_frac_c.append(rfc) # renorm_frac_i.append(rfi) # renorm_frac_o.append(rfo) # # if verbose: # print('Total fraction of flux captured by IFU: '+str(np.round(ifu_frac * 100,1))+'%') # print('----------------------------------------------') # print('Contribution from central fibre: '+str(np.round(rfc * 100,1))+'%') # print('Contribution from inner-ring fibres: '+str(np.round(rfi * 100,1))+'%') # print('Contribution from outer-ring fibres: '+str(np.round(rfo * 100,1))+'%') # print # # else: # # fwhm = seeing # if verbose: # print('Simulating '+str(fwhm)+'" seeing...') # #calculate 2-dim Gaussian flux distribution as function of input FWHM # fx = np.exp(-(np.absolute(xx) * cons / fwhm) ** 2.0) # fy = np.exp(-(np.absolute(yy) * cons / fwhm) ** 2.0) # f = fx * fy * 1e6 # # frac_c = np.sum(f[central]) / np.sum(f) # frac_i = 6. * np.sum(f[inner]) / np.sum(f) # frac_o = 6. * ( np.sum(f[outer1]) + np.sum(f[outer2]) ) / np.sum(f) # frac_ifu = frac_c + frac_i + frac_o #this is slightly overestimated (<1%) because they are not actually circular fibres # # renorm_frac_c = frac_c / frac_ifu # renorm_frac_i = frac_i / frac_ifu # renorm_frac_o = frac_o / frac_ifu # # if verbose: # print('Total fraction of flux captured by IFU: '+str(np.round(frac_ifu * 100,1))+'%') # print('----------------------------------------------') # print('Contribution from central fibre: '+str(np.round(renorm_frac_c * 100,1))+'%') # print('Contribution from inner-ring fibres: '+str(np.round(renorm_frac_i * 100,1))+'%') # print('Contribution from outer-ring fibres: '+str(np.round(renorm_frac_o * 100,1))+'%') # print flux_ratios['seeing'] = [] flux_ratios['frac_ifu'] = [] flux_ratios['central'] = [] flux_ratios['inner'] = [] flux_ratios['outer'] = [] flux_ratios['outer1'] = [] flux_ratios['outer2'] = [] #stupid python!!! for fwhm in get_iterable(seeing): if verbose: print('Simulating ' + str(fwhm) + '" seeing...') #calculate 2-dim Gaussian flux distribution as function of input FWHM fx = np.exp(-(np.absolute(xx) * cons / fwhm)**2.0) fy = np.exp(-(np.absolute(yy) * cons / fwhm)**2.0) #f = fx * fy f = fx * fy * 1e6 frac_c = np.sum(f[central]) / np.sum(f) frac_i = 6. * np.sum(f[inner]) / np.sum(f) frac_o1 = 6. * np.sum(f[outer1]) / np.sum(f) frac_o2 = 6. * np.sum(f[outer2]) / np.sum(f) frac_o = 6. * (np.sum(f[outer1]) + np.sum(f[outer2])) / np.sum(f) ifu_frac = frac_c + frac_i + frac_o #this is slightly overestimated (<1%) because they are not actually circular fibres #renormalized fractions (renormalized to the total flux captured by the IFU, not the TOTAL total flux) rfc = frac_c / ifu_frac rfi = frac_i / ifu_frac rfo = frac_o / ifu_frac rfo1 = frac_o1 / ifu_frac rfo2 = frac_o2 / ifu_frac #fill the output dictionary flux_ratios['seeing'].append(fwhm) flux_ratios['frac_ifu'].append(ifu_frac) flux_ratios['central'].append(rfc) flux_ratios['inner'].append(rfi) flux_ratios['outer'].append(rfo) flux_ratios['outer1'].append(rfo1) flux_ratios['outer2'].append(rfo2) if verbose: print('Total fraction of flux captured by IFU: ' + str(np.round(ifu_frac * 100, 1)) + '%') print('----------------------------------------------') print('Contribution from central fibre: ' + str(np.round(rfc * 100, 1)) + '%') print('Contribution from inner-ring fibres: ' + str(np.round(rfi * 100, 1)) + '%') print('Contribution from outer-ring fibres: ' + str(np.round(rfo * 100, 1)) + '%') print return flux_ratios