def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope(temperature=pre_encode(280.0*u.K)) self.spectrograph = Spectrograph() self.exposure = Exposure() self.telescope.add_spectrograph(self.spectrograph) self.spectrograph.add_exposure(self.exposure) #set interface variables self.templates = ['flam', 'qso', 's99', 'o5v', 'g2v', 'g191b2b', 'gd71', 'gd153', 'ctts', 'mdwarf', 'orion', 'nodust', 'ebv6', 'hi1hei1', 'hi0hei1'] self.template_options = [SpectralLibrary[t] for t in self.templates] self.help_text = help_text self.gratings = self.spectrograph.modes self.grating_options = [self.spectrograph.descriptions[g] for g in self.gratings] self.dl_filename = "" #set default input values self.update_exposure() #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False
def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope() self.camera = Camera() self.exposure = Exposure() self.telescope.add_camera(self.camera) self.camera.add_exposure(self.exposure) #set interface variables self.templates = [ 'fab', 'bb', 'o5v', 'b5v', 'g2v', 'm2v', 'orion', 'elliptical', 'sbc', 'starburst', 'ngc1068' ] self.template_options = [SpectralLibrary[t] for t in self.templates] self.help_text = help_text self.snr_hover_tooltip = hover_tooltip.format("S/N") self.mag_hover_tooltip = hover_tooltip.format("Magnitude") self.exp_hover_tooltip = hover_tooltip.format("Exptime") #update default exposure based on tool_defaults self.update_exposure() #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False
def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #set up holoviews & load data for datashader #note that right now the data is read from a pickle, not loaded separately; #I'm leaving load_dataset.py in the directory, but it's not used currently self.dataframe = pandas.read_pickle( os.path.join(basedir, 'data', 'cmd_frame_large_no_noise.pkl')) self.cmap_picker = ColormapPicker(rename={'colormap': 'cmap'}, name='') #initialize engine objects self.telescope = Telescope() self.camera = Camera() self.exposure = Exposure() self.telescope.add_camera(self.camera) self.camera.add_exposure(self.exposure) #set interface variables self.help_text = help_text #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False
def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope(temperature=pre_encode(280.0 * u.K)) self.spectrograph = Spectropolarimeter() self.exposure = Exposure() self.telescope.add_spectrograph(self.spectrograph) self.spectrograph.add_exposure(self.exposure)
def test_camera(): c = Camera() t = Telescope() t.aperture = pre_encode(15.08 * u.m) t.add_camera(c) sval = [49.70, 11.58, 257.88, 530.75, 962.04, 2075.26, 1819.15, 8058.30, 11806.49, 15819.07] expt = 3600. * u.s qe = c.recover('total_qe') fsky = c._fsky() wav = c.recover("pivotwave") print("Sky counts:") for i in range(c.n_bands): skc = (fsky[i] * expt * qe[i]).value rat = skc / sval[i] * 100. print("{:8.2f} ({:5.1f}% of spreadsheet value) at {:4.0f}".format(skc, rat, wav[i]))
def test_specetc(print_models=True): print("\n\n==Instantiating models==") print("(setting verbose = True)\n") e, s, t = SpectrographicExposure(), Spectrograph(), Telescope() t.add_spectrograph(s) s.add_exposure(e) e.verbose = True print(e.zmin, e.zmax) if print_models: print("\n\n==Parameter values (aka defaults):==") print("\n=TELESCOPE=\n") for attr in t._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(t,attr)))) #pprint(t.encode(), indent=2) print("\n=SPECTROGRAPH=\n") for attr in s._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(s,attr)))) #pprint(s.encode(), indent=2) print("\n=EXPOSURE=\n") for attr in e._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(e,attr)))) #pprint(e.encode(), indent=2) print("\n\nSetting spectrograph mode to 'G300M'") s.mode = "G300M" print("\n\n==Setting SED to QSO at z=0.04 & recalculating==\n") e.disable() #since we're updating more than one parameter, turn off calculations e.sed_id = 'qso' e.redshift = 1.0 e.enable() #turn calculations back on again print("\n\n==Current unknown: {}==".format(e.unknown)) print("\nSNR: {}".format(e.recover('snr'))) print("No other unknown types currently supported.") """
def test_photetc(print_models=True): print("\n\n==Instantiating models==") print("(setting verbose = True)\n") e, c, t = PhotometricExposure(), Camera(), Telescope() t.add_camera(c) c.add_exposure(e) e.verbose = True if print_models: print("\n\n==Parameter values (aka defaults):==") print("\n=TELESCOPE=\n") for attr in t._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(t,attr)))) #pprint(t.encode(), indent=2) print("\n=CAMERA=\n") for attr in c._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(c,attr)))) #pprint(c.encode(), indent=2) print("\n=EXPOSURE=\n") for attr in e._tracked_attributes: print('{}: {}'.format(attr, str_jsunit(getattr(e, attr)))) #pprint(e.encode(), indent=2) print("\n\n==Setting spectrum to QSO & recalculating==\n") e.sed_id = 'qso' print("\n\n==Current unkown: {}==".format(e.unknown)) print("\nSNR: {}".format(e.snr)) print("\n\n==Setting unknown to 'magnitude'==\n") e.unknown = "magnitude" print("\nMagnitude: {}".format(e.magnitude)) print("\n\n==Setting unknown to 'exptime'==\n") e.unknown = "exptime" print("\nExptime: {}".format(e.exptime))
class HDI_ETC(SYOTool): tool_prefix = "hdi" save_models = ["telescope", "camera", "exposure"] save_params = { "exptime": None, #slider value "snratio": None, #slider value "renorm_magnitude": None, #slider value "spectrum_type": ("exposure", "sed_id"), "aperture": ("telescope", "aperture"), "user_prefix": None } save_dir = os.path.join(os.environ['LUVOIR_SIMTOOLS_DIR'], 'saves') #must include this to set defaults before the interface is constructed tool_defaults = { 'exptime': pre_encode(1.0 * u.hour), 'snratio': pre_encode(30.0 * u.electron**0.5), 'renorm_magnitude': pre_encode(30.0 * u.mag('AB')), 'aperture': pre_encode(12.0 * u.m), 'spectrum_type': 'fab' } def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope() self.camera = Camera() self.exposure = Exposure() self.telescope.add_camera(self.camera) self.camera.add_exposure(self.exposure) #set interface variables self.templates = [ 'fab', 'bb', 'o5v', 'b5v', 'g2v', 'm2v', 'orion', 'elliptical', 'sbc', 'starburst', 'ngc1068' ] self.template_options = [SpectralLibrary[t] for t in self.templates] self.help_text = help_text self.snr_hover_tooltip = hover_tooltip.format("S/N") self.mag_hover_tooltip = hover_tooltip.format("Magnitude") self.exp_hover_tooltip = hover_tooltip.format("Exptime") #update default exposure based on tool_defaults self.update_exposure() #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False def tool_postinit(self): """ Need to disable the SNR slider to start with. """ self.refs["snr_slider"].disabled = True #Control methods def tab_change(self, attr, old, new): """ Whenever tabs are switched: - Disable the appropriate input slider(s) - Set self.exposure.unknown, if appropriate """ active_tab = new['value'][0] all_inputs = [ "ap_slider", "exp_slider", "mag_slider", "snr_slider", "template_select" ] inactive = [("snr_slider", ), ("exp_slider", ), ("mag_slider", ), ("ap_slider", "exp_slider", "snr_slider")][active_tab] for inp in all_inputs: self.refs[inp].disabled = inp in inactive #set the correct exposure unknown: if active_tab < 3: self.exposure.unknown = ["snr", "exptime", "magnitude"][active_tab] self.controller(None, None, None) def controller(self, attr, old, new): #Grab values from the inputs self.exptime = pre_encode(self.refs["exp_slider"].value * u.hour) self.renorm_magnitude = pre_encode(self.refs["mag_slider"].value * u.mag('AB')) self.snratio = pre_encode(self.refs["snr_slider"].value * u.electron**0.5) self.aperture = pre_encode(self.refs["ap_slider"].value * u.m) temp = self.template_options.index(self.refs["template_select"].value) self.spectrum_type = self.templates[temp] self.update_exposure() snr = self._snr mag = self._mag exp = self._exp pwave = self._pivotwave #Update the plots' y-range bounds self.refs["snr_figure"].y_range.start = 0 self.refs["snr_figure"].y_range.end = max(1.3 * snr[:-1].max(), 5.) self.refs["exp_figure"].y_range.start = 0 self.refs["exp_figure"].y_range.end = max(1.3 * exp[:-1].max(), 2.) self.refs["mag_figure"].y_range.start = mag.max() + 5. self.refs["mag_figure"].y_range.end = mag.min() - 5. self.refs[ "sed_figure"].y_range.start = self.spectrum_template.flux.max( ) + 5. self.refs["sed_figure"].y_range.end = self.spectrum_template.flux.min( ) - 5. #Update source data self.refs["snr_source_blue"].data = { 'x': pwave[2:-3], 'y': snr[2:-3], 'desc': self.camera.bandnames[2:-3] } self.refs["exp_source_blue"].data = { 'x': pwave[2:-3], 'y': exp[2:-3], 'desc': self.camera.bandnames[2:-3] } self.refs["mag_source_blue"].data = { 'x': pwave[2:-3], 'y': mag[2:-3], 'desc': self.camera.bandnames[2:-3] } self.refs["snr_source_orange"].data = { 'x': pwave[:2], 'y': snr[:2], 'desc': self.camera.bandnames[:2] } self.refs["exp_source_orange"].data = { 'x': pwave[:2], 'y': exp[:2], 'desc': self.camera.bandnames[:2] } self.refs["mag_source_orange"].data = { 'x': pwave[:2], 'y': mag[:2], 'desc': self.camera.bandnames[:2] } self.refs["snr_source_red"].data = { 'x': pwave[-3:], 'y': snr[-3:], 'desc': self.camera.bandnames[-3:] } self.refs["exp_source_red"].data = { 'x': pwave[-3:], 'y': exp[-3:], 'desc': self.camera.bandnames[-3:] } self.refs["mag_source_red"].data = { 'x': pwave[-3:], 'y': mag[-3:], 'desc': self.camera.bandnames[-3:] } self.refs["spectrum_template"].data = { 'x': self.template_wave, 'y': self.template_flux } def update_exposure(self): """ Update the exposure's parameters and recalculate everything. """ #We turn off calculation at the beginning so we can update everything #at once without recalculating every time we change something self.exposure.disable() #Update all the parameters self.exposure.n_exp = 3 self.exposure.exptime = pre_decode(self.exptime) self.exposure.snr = pre_decode(self.snratio) self.exposure.sed_id = self.spectrum_type self.telescope.aperture = self.aperture self.exposure.renorm_sed(pre_decode(self.renorm_magnitude)) #Now we turn calculations back on and recalculate self.exposure.enable() #Set the spectrum template self.spectrum_template = pre_decode(self.exposure.sed) @property def template_wave(self): return self.exposure.recover('sed').wave @property def template_flux(self): return self.exposure.recover('sed').flux #Conversions to avoid Bokeh Server trying to serialize Quantities @property def _pivotwave(self): return self.camera.recover('pivotwave').value @property def _snr(self): return self.exposure.recover('snr').value @property def _mag(self): return self.exposure.recover('magnitude').value @property def _exp(self): exp = self.exposure.recover('exptime').to(u.h) return exp.value def update_toggle(self, active): if active: self.refs["user_prefix"].value = self.user_prefix self.refs["user_prefix"].disabled = True self.refs["save_button"].disabled = False self.overwrite_save = True else: self.refs["user_prefix"].disabled = False self.refs["save_button"].disabled = False self.overwrite_save = False #Save and Load def save(self): """ Save the current calculations. """ #Check for an existing save file if we're overwriting if self.overwrite_save and self.current_savefile: self.current_savefile = self.save_file(self.current_savefile, overwrite=True) else: #Set the user prefix from the bokeh interface prefix = self.refs["user_prefix"].value if not prefix.isalpha() or len(prefix) < 3: self.refs["save_message"].text = "Please include a prefix of at "\ "least 3 letters (and no other characters)." return self.user_prefix = prefix #Save the file: self.current_savefile = self.save_file() #Tell the user the filename or the error message. if not self.current_savefile: self.refs["save_message"].text = "Save unsuccessful; please " \ "contact the administrators." return self.refs["save_message"].text = "This calculation was saved with " \ "the ID {}".format(self.current_savefile) self.refs["update_save"].disabled = False def load(self): # Get the filename from the bokeh interface calcid = self.refs["load_filename"].value #Load the file code = self.load_file(calcid) if not code: #everything went fine #Update the interface self.refs["update_save"].disabled = False self.current_save = calcid self.refs["exp_slider"].value = pre_decode(self.exptime).value self.refs["mag_slider"].value = pre_decode( self.renorm_magnitude).value self.refs["ap_slider"].value = pre_decode(self.aperture).value self.refs["snr_slider"].value = pre_decode(self.snratio).value temp = self.templates.index(self.spectrum_type) self.refs["template_select"].value = self.template_options[temp] self.controller(None, None, None) errmsg = [ "Calculation ID {} loaded successfully.".format(calcid), "Calculation ID {} does not exist, please try again.".format( calcid), "Load unsuccessful; please contact the administrators.", "There was an error restoring the save state; please contact" " the administrators." ][code] if code == 0 and self.load_mismatch: errmsg += "<br><br><b><i>Load Mismatch Warning:</b></i> "\ "The saved model parameters did not match the " \ "parameter values saved for this tool. Saved " \ "model values were given priority." self.refs["load_message"].text = errmsg
class POLLUX_IMAGE(SYOTool): tool_prefix = "pollux" save_models = [ "telescope", "camera", "spectrograph", "spectropolarimeter", "exposure" ] save_params = { "redshift": None, #slider value "renorm_magnitude": None, #slider value "exptime": None, #slider value "grating": ("spectropolarimeter", "mode"), #drop-down selection "aperture": ("telescope", "aperture"), #slider value "spectrum_type": ("exposure", "sed_id"), #drop-down selection "user_prefix": None } save_dir = os.path.join(os.environ['LUVOIR_SIMTOOLS_DIR'], 'saves') #must include this to set defaults before the interface is constructed tool_defaults = { 'redshift': pre_encode(0.25 * u.dimensionless_unscaled), 'renorm_magnitude': pre_encode(21.0 * u.mag('AB')), 'exptime': pre_encode(1.0 * u.hour), 'grating': "NUV_POL", 'aperture': pre_encode(15.0 * u.m), 'spectrum_type': 'qso' } def __init__(self): print('Initializing the parameters ...') self.grating = grating_pollux self.aperture = ap_pollux self.exptime = expt_pollux self.redshift = pre_encode(red_pollux[1]['value']) self.renorm_magnitude = mag_pollux self.spectrum_type = spectrum_pollux self.tool_preinit() self.photon_count = photon_count self.EM_gain = gain self.show_plot = show_plot self.file_name = file_name if snr_pollux == 1: self.make_image = False self.update_exposure() self.plot_snr() print('Saving the file at', basedir, '/pollux_tool/files/') np.savetxt( basedir + '/pollux_tool/files/' + self.file_name + '_' + self.grating + '_snr_data.txt', np.array((self.background_wave, self._snr)).T) if image_pollux == 1: self.make_image = True self.update_exposure() self.plot_image() print('Saving the file at', basedir, '/pollux_tool/files/') np.savetxt( basedir + '/pollux_tool/files/' + self.file_name + '_' + self.grating + '_2dimage_data.txt', self._final_image) np.savetxt( basedir + '/pollux_tool/files/' + self.file_name + '_' + self.grating + '_2dimage_wavelength.txt', self._wave_image) def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope(temperature=pre_encode(280.0 * u.K)) self.spectrograph = Spectropolarimeter() self.exposure = Exposure() self.telescope.add_spectrograph(self.spectrograph) self.spectrograph.add_exposure(self.exposure) #set interface variables tool_postinit = None def update_exposure(self): """ Update the exposure's parameters and recalculate everything. """ #We turn off calculation at the beginning so we can update everything #at once without recalculating every time we change something #Update all the parameters self.telescope.aperture = self.aperture self.spectrograph.mode = self.grating self.exposure.exptime = pre_decode(self.exptime) self.exposure.redshift = pre_decode(self.redshift) self.exposure.sed_id = self.spectrum_type self.exposure.renorm_sed(pre_decode(self.renorm_magnitude), bandpass='******') #Now we turn calculations back on and recalculate self.exposure.enable(make_image=self.make_image, photon_count=self.photon_count, gain=self.EM_gain) #Set the spectrum template self.spectrum_template = pre_decode(self.exposure.sed) def plot_snr(self): """ """ plt.figure(1) plt.subplot(211) plt.plot(self.template_wave, self.template_flux, '-b', label='source') plt.plot(self.background_wave, self.background_flux, '--k', label='background') plt.legend(loc='upper right', fontsize='large', frameon=False) plt.xlim(900, 4000.) #plt.xlabel('Wavelangth [A]', fontsize='x-large') plt.ylabel('Flux [erg/s/cm^2/A]', fontsize='x-large') plt.subplot(212) plt.plot(self.background_wave, self._snr, '-b') plt.xlim(900, 4000.) plt.xlabel('Wavelangth [A]', fontsize='x-large') plt.ylabel('S/N per resel', fontsize='x-large') if show_plot == 1: print('Saving the image at', basedir, '/pollux_tool/plots/') plt.savefig(basedir + '/pollux_tool/plots/' + self.file_name + '_' + self.grating + '_snr_plot.png', dpi=300) plt.show() def plot_image(self): """ """ #if self.grating[0] == 'F': #plt.imshow(np.log10(self._final_image), origin='lower left', cmap='hot', interpolation='none',aspect='auto') #else: plt.imshow(self._final_image, origin='lower left', cmap='hot', interpolation='none', aspect='auto') plt.colorbar() if show_plot == 1: print('Saving the image at', basedir, '/pollux_tool/plots/') plt.savefig(basedir + '/pollux_tool/plots/' + self.file_name + '_' + self.grating + '_2dimage_plot.png', dpi=300) plt.show() @property def template_wave(self): """ Easy SED wavelength access for the Bokeh widgets. """ return self.exposure.recover('sed').wave @property def template_flux(self): """ Easy SED flux access for the Bokeh widgets. """ sed = self.exposure.recover('sed') wave = sed.wave * u.Unit(sed.waveunits.name) if sed.fluxunits.name == "abmag": funit = u.ABmag elif sed.fluxunits.name == "photlam": funit = u.ph / u.s / u.cm**2 / u.AA else: funit = u.Unit(sed.fluxunits.name) flux = (sed.flux * funit).to(u.erg / u.s / u.cm**2 / u.AA, equivalencies=u.spectral_density(wave)) return flux.value @property def background_wave(self): """ Easy instrument wavelength access for the Bokeh widgets. """ bwave = self.spectrograph.recover('wave').to(u.AA) return bwave.value @property def background_flux(self): """ Easy instrument background flux access for the Bokeh widgets. """ bef = self.spectrograph.recover('bef').to(u.erg / u.s / u.cm**2 / u.AA) return bef.value @property def _snr(self): return np.nan_to_num(self.exposure.recover('snr').value) @property def _final_image(self): return np.nan_to_num(self.exposure.recover('final_image')) @property def _wave_image(self): return np.nan_to_num(self.exposure.recover('wave_image'))
fill_alpha=0.2, fill_color='black', line_alpha=0.2, line_width=1, line_color='black') plot.state.text('textx', 'texty', 'text', source=confusion_limit_source, text_align='left', text_color='black') ########################################### # CREATE THE SYOTOOLS ELEMENTS AND MODELS # ########################################### e, c, t = PhotometricExposure(), Camera(), Telescope() t.add_camera(c) t.aperture = 15.0 * u.meter c.add_exposure(e) e.sed_id = 'fab' t.aperture = 15. * u.meter e.exptime = 3600. * u.s new_snrs = np.arange(5) # derives the S/N at various magnitude limits for ii in [0, 1, 2, 3, 4]: e.renorm_sed(mag_label_source.data['mags'][ii] * u.ABmag) new_snrs[ii] = e.snr[1]['value'][4] # results of ETC are stored weirdly as a list of AB mags = e.magnitude[1]['value'][:] etc_label_source = ColumnDataSource( data={
class LUMOS_ETC(SYOTool): tool_prefix = "lumos" save_models = ["telescope", "camera", "spectrograph", "exposure"] save_params = {"redshift": None, #slider value "renorm_magnitude": None, #slider value "exptime": None, #slider value "grating": ("spectrograph", "mode"), #drop-down selection "aperture": ("telescope", "aperture"), #slider value "spectrum_type": ("exposure", "sed_id"), #drop-down selection "user_prefix": None} save_dir = os.path.join(os.environ['LUVOIR_SIMTOOLS_DIR'],'saves') #must include this to set defaults before the interface is constructed tool_defaults = {'redshift': pre_encode(0.0 * u.dimensionless_unscaled), 'renorm_magnitude': pre_encode(21.0 * u.mag('AB')), 'exptime': pre_encode(1.0 * u.hour), 'grating': "G120M", 'aperture': pre_encode(15.0 * u.m), 'spectrum_type': 'qso'} def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #initialize engine objects self.telescope = Telescope(temperature=pre_encode(280.0*u.K)) self.spectrograph = Spectrograph() self.exposure = Exposure() self.telescope.add_spectrograph(self.spectrograph) self.spectrograph.add_exposure(self.exposure) #set interface variables self.templates = ['flam', 'qso', 's99', 'o5v', 'g2v', 'g191b2b', 'gd71', 'gd153', 'ctts', 'mdwarf', 'orion', 'nodust', 'ebv6', 'hi1hei1', 'hi0hei1'] self.template_options = [SpectralLibrary[t] for t in self.templates] self.help_text = help_text self.gratings = self.spectrograph.modes self.grating_options = [self.spectrograph.descriptions[g] for g in self.gratings] self.dl_filename = "" #set default input values self.update_exposure() #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False tool_postinit = None def update_exposure(self): """ Update the exposure's parameters and recalculate everything. """ #We turn off calculation at the beginning so we can update everything #at once without recalculating every time we change something self.exposure.disable() #Update all the parameters self.telescope.aperture = self.aperture self.spectrograph.mode = self.grating self.exposure.exptime = pre_decode(self.exptime) self.exposure.redshift = pre_decode(self.redshift) self.exposure.sed_id = self.spectrum_type self.exposure.renorm_sed(pre_decode(self.renorm_magnitude), bandpass='******') #Now we turn calculations back on and recalculate self.exposure.enable() #Set the spectrum template self.spectrum_template = pre_decode(self.exposure.sed) @property def template_wave(self): """ Easy SED wavelength access for the Bokeh widgets. """ return self.exposure.recover('sed').wave @property def template_flux(self): """ Easy SED flux access for the Bokeh widgets. """ sed = self.exposure.recover('sed') wave = sed.wave * u.Unit(sed.waveunits.name) if sed.fluxunits.name == "abmag": funit = u.ABmag elif sed.fluxunits.name == "photlam": funit = u.ph / u.s / u.cm**2 / u.AA else: funit = u.Unit(sed.fluxunits.name) flux = (sed.flux * funit).to(u.erg / u.s / u.cm**2 / u.AA, equivalencies=u.spectral_density(wave)) return flux.value @property def background_wave(self): """ Easy instrument wavelength access for the Bokeh widgets. """ bwave = self.spectrograph.recover('wave').to(u.AA) return bwave.value @property def background_flux(self): """ Easy instrument background flux access for the Bokeh widgets. """ bef = self.spectrograph.recover('bef').to(u.erg / u.s / u.cm**2 / u.AA) return bef.value @property def _snr(self): return np.nan_to_num(self.exposure.recover('snr').value) def controller(self, attr, old, new): """ Callback to recalculate everything for the figures whenever an input's value is changed """ #Grab values from the inputs self.exptime = pre_encode(self.refs["exp_slider"].value * u.hour) self.renorm_magnitude = pre_encode(self.refs["mag_slider"].value * u.mag('AB')) self.redshift = pre_encode(self.refs["red_slider"].value) self.aperture = pre_encode(self.refs["ap_slider"].value * u.m) temp = self.template_options.index(self.refs["template_select"].value) self.spectrum_type = self.templates[temp] grat = self.grating_options.index(self.refs["grating_select"].value) self.grating = self.gratings[grat] #update the exposure and grab all our relevant values. self.update_exposure() snr = self._snr #SNR at bwave bwave, bflux = self.background_wave, self.background_flux twave, tflux = self.template_wave, self.template_flux self.refs["flux_yrange"].start = 0. self.refs["flux_yrange"].end = 1.5 * tflux.max() #self.refs["flux_xrange"].start = min(bwave.min(), twave.min()) #self.refs["flux_xrange"].end = max(bwave.max(), twave.max()) self.refs["snr_yrange"].start = 0. self.refs["snr_yrange"].end = 1.5 * snr.max() #self.refs["snr_xrange"].start = min(bwave.min(), twave.min()) #self.refs["snr_xrange"].end = max(bwave.max(), twave.max()) self.refs["snr_source"].data = {'y': snr, 'x': bwave} self.refs["spectrum_template"].data = {'x': twave, 'y': tflux} self.refs["instrument_background"].data = {'x':bwave, 'y':bflux} self.refs["red_slider"].start = self.exposure.zmin self.refs["red_slider"].end = self.exposure.zmax def dl_change_filename(self, attr, old, new): self.dl_filename = self.refs["dl_textinput"].value self.refs["dl_format_button_group"].active = None def dl_execute(self, *arg): which_format = self.refs["dl_format_button_group"].active ext = ['.txt', '.fits'][which_format] fmt = ['ascii', 'fits'][which_format] outfile = self.dl_filename + ext self.refs["dl_linkbox"].text = "Please wait..." twave = self.template_wave swave = self.background_wave snr = self._snr out_snr = np.interp(twave, swave, snr, left=0., right=0.) out_table = Table([twave, self.template_flux, out_snr], names=('wave','flux','sn')) out_table.write(outfile, format=fmt, overwrite=True) os.system('gzip -f ' + outfile) os.system('cp -rp ' + outfile + '.gz /home/jtastro/jt-astro.science/outputs') out_msg = "Your file is <a href='http://jt-astro.science/outputs/{0}.gz'>{0}.gz</a>. " self.refs["dl_linkbox"].text = out_msg.format(outfile)
class CMD(SYOTool): tool_prefix = "cmd" save_models = ["telescope", "camera", "exposure"] save_params = { "exptime": None, #slider value #NOT YET UPDATED "snratio": None, #slider value "age": None, #slider value "crowding": None, #slider value "distance": None, #slider value, "metallicity": None, #slider value "noise": None, #slider value "spectrum_type": ("exposure", "sed_id"), "aperture": ("telescope", "aperture"), "user_prefix": None } save_dir = os.path.join(os.environ['LUVOIR_SIMTOOLS_DIR'], 'saves') #must include this to set defaults before the interface is constructed tool_defaults = { 'exptime': pre_encode(3600.0 * u.s), 'snratio': pre_encode(30.0 * u.electron**0.5), 'age': pre_encode(u.Dex(10.0 * u.Gyr)), 'crowding': pre_encode(20.0 * u.dimensionless_unscaled), #u.ABmag / u.arcsec**2), 'distance': pre_encode(1.0 * u.Mpc), 'metallicity': pre_encode(u.Dex(0.0)), 'noise': pre_encode(500.0 * u.dimensionless_unscaled), 'aperture': pre_encode(15.0 * u.m), 'spectrum_type': 'fab' } verbose = True def tool_preinit(self): """ Pre-initialize any required attributes for the interface. """ #set up holoviews & load data for datashader #note that right now the data is read from a pickle, not loaded separately; #I'm leaving load_dataset.py in the directory, but it's not used currently self.dataframe = pandas.read_pickle( os.path.join(basedir, 'data', 'cmd_frame_large_no_noise.pkl')) self.cmap_picker = ColormapPicker(rename={'colormap': 'cmap'}, name='') #initialize engine objects self.telescope = Telescope() self.camera = Camera() self.exposure = Exposure() self.telescope.add_camera(self.camera) self.camera.add_exposure(self.exposure) #set interface variables self.help_text = help_text #Formatting & interface stuff: self.format_string = interface_format self.interface_file = os.path.join(script_dir, "interface.yaml") #For saving calculations self.current_savefile = "" self.overwrite_save = False def tool_postinit(self): #update default exposure based on tool_defaults self.update_exposure_params() #Calculation methods @staticmethod def add_noise(new_frame, noise_scale): rmag = new_frame.rmag gmag = new_frame.gmag noise_basis = 10. / 10.**((30. + rmag) / 5.) # mind the 10! r_noise = np.random.normal(0.0, noise_scale, np.size(rmag)) * noise_basis g_noise = np.random.normal(0.0, noise_scale, np.size(rmag)) * noise_basis new_frame.rmag = rmag + r_noise new_frame.gmag = gmag + g_noise new_frame.grcolor = np.clip(rmag - gmag, a_min=-1.2, a_max=4.2) return new_frame def select_dataframe(self, metallicity, age): selection = lambda frame: (frame.metalindex == metallicity) & ( frame.ageindex == age) return self.dataframe.loc[selection] def select_stars( self, obj, age, metallicity, noise ): # received "obj" of type "Points" and "age" of type ordinary float #"obj" incoming is the points object if self.verbose: print("age / metallicity / noise inside select_stars", age, metallicity, noise) new_frame = self.select_dataframe(metallicity, age) noise_frame = self.add_noise(new_frame, float(noise)) cmd_points = hv.Points(noise_frame, kdims=['grcolor', 'rmag']) return cmd_points @property def _derived_snrs(self): new_snrs = np.zeros(5, dtype=float) mags = self.refs["cmd_mag_source"].data["mags"] for m, mag in enumerate(mags): self.exposure.renorm_sed(mag * u.ABmag) new_snrs[m] = self.exposure.recover('snr')[4].value return new_snrs @property def _derived_snrs_labels(self): return self._derived_snrs.astype('|S4') def crowding_limit(self, crowding_apparent_magnitude, distance): """ Calculate the crowding limit. """ aperture = pre_decode(self.aperture) g_solar = 4.487 stars_per_arcsec_limit = 10. * (aperture / 2.4)**2 # (JD1) distmod = 5. * np.log10((distance + 1e-5) * 1e6) - 5. #why this fudge? g = np.array( -self.dataframe.gmag ) # absolute magnitude in the g band - the -1 corrects for the sign flip in load_datasets (for plotting) g.sort( ) # now sorted from brightest to faintest in terms of absolute g magnitude luminosity = 10.**(-0.4 * (g - g_solar)) total_luminosity_in_lsun = np.sum(luminosity) number_of_stars = np.full_like( g, 1.0 ) # initial number of stars in each "bin" - a list of 10,000 stars total_absolute_magnitude = -2.5 * np.log10( total_luminosity_in_lsun) + g_solar apparent_brightness_at_this_distance = total_absolute_magnitude + distmod scale_factor = 10.**(-0.4 * (crowding_apparent_magnitude - apparent_brightness_at_this_distance)) cumulative_number_of_stars = np.cumsum( scale_factor * number_of_stars) # the cumulative run of luminosity in Lsun crowding_limit = np.interp(stars_per_arcsec_limit.value, cumulative_number_of_stars, g) return crowding_limit #Control methods def quantize_slider(self, ref, precision, minval, step): """ A utility function for quantizing the value of a slider. """ val = Decimal(self.refs[ref].value).quantize(Decimal(precision)) return int( ((val - Decimal(minval)) / Decimal(step)).to_integral_exact()) def age_slider_update(self): """ Send an event to the age stream. """ new_age = self.quantize_slider("age_slider", '0.01', '5.5', '0.05') self.refs["age_stream"].event(age=new_age) if self.verbose: print("Age selected by slider = {}".format(new_age)) def metallicity_slider_update(self): """ Send an event to the metallicity stream. Using Decimal to handle quantization. """ new_metallicity = self.quantize_slider("metallicity_slider", '0.1', '-2.0', '0.5') self.refs["metallicity_stream"].event(metallicity=new_metallicity) if self.verbose: print( "Metallicity selected by slider = {}".format(new_metallicity)) def noise_slider_update(self): """ Send an event to the noise stream. This slider is in steps of 50, so we can just quantize with int() instead of using Decimal. """ ival = int(self.refs["noise_slider"].value) if self.verbose: print("Inside noise_slider, will scale to: {}".format(ival)) self.refs["noise_stream"].event(noise=ival) def distance_slider_update(self): """ Update the magnitude label source for the new distance. """ val = self.refs["distance_slider"].value distmod = 5. * np.log10((val + 1e-5) * 1e6) - 5. new_mags = distmod + np.array([10., 5., 0., -5., -10.]) mlsource = self.refs['cmd_mag_source'] mlsource.data['mags'] = new_mags mlsource.data['text'] = new_mags.astype('|S4') def crowding_slider_update(self): """ Update the crowding from the slider input. """ crowding_mag = self.refs["crowding_slider"].value distance = self.refs["distance_slider"].value nstars_per_arcsec = int(10. * (self.refs['ap_slider'].value / 2.4)**2) crowding_limit = self.crowding_limit(crowding_mag, distance) confsource = self.refs["cmd_conf_source"] confsource.data = { 'top': [-crowding_limit], 'textx': [-0.8], 'texty': [-crowding_limit - 0.2], 'text': ['Crowding: ({} stars / sq. arsec)'.format(nstars_per_arcsec)] } def update_exposure_params(self): """ Update the exposure parameters. """ new_aper = self.refs["ap_slider"].value * u.m new_expt = (self.refs["exp_slider"].value * u.h).to(u.s) new_snr = self.refs["snr_slider"].value * u.electron**0.5 self.aperture = pre_encode(new_aper) self.exptime = pre_encode(new_expt) self.snratio = pre_encode(new_snr) self.telescope.aperture = self.aperture def exposure_update(self): """ Update the exposure when sliders are updated. """ self.update_exposure_params() self.exposure.unknown = 'snr' self.exposure.exptime = pre_decode(self.exptime) new_snrs = self._derived_snrs etcsource = self.refs["cmd_etc_source"] mlsource = self.refs["cmd_mag_source"] etcsource.data = { "mags": mlsource.data["mags"], "snr": new_snrs, "snr_label": new_snrs.astype('|S4'), 'x': np.full(5, 3.2).tolist(), 'y': np.arange(-10.4, 10., 5., dtype=float).tolist() } if self.verbose: print("mag_values in exposure_update: {}".format( etcsource.data['y'])) print("new_snrs in exposure_update: {}".format( etcsource.data['snr'])) noise_scale_factor = int( 10000. / new_snrs[1] ) # divide an number by the S/N at AB = 5 absolute - set up to make it come out right self.refs["noise_stream"].event(noise=int(noise_scale_factor)) def sn_slider_update(self): """ Update exposure when SNR slider is changed. """ new_sn = pre_decode(self.snratio) new_expt = pre_decode(self.exptime) #new_sn = self.refs["snr_slider"].value #new_expt = self.refs["exp_slider"].value if self.verbose: print("calling sn_updater with sn = {} and exptime {}".format( new_sn, new_expt)) self.exposure.unknown = "magnitude" self.exposure.exptime = new_expt #pre_decode(self.exptime) self.exposure.snr = new_sn #pre_decode(self.snratio) vmag = self.exposure.recover('magnitude')[4].value distance = pre_decode(self.distance) distmod = 5. * np.log10((distance.value + 1e-5) * 1e6) - 5. lmsource = self.refs["cmd_lim_source"] lmsource.data = { 'mags': [distmod - vmag - 0.4], 'maglabel': ["{:4.1f}".format(vmag)], 'sn': ["{}".format(new_sn.value)], 'x_mag': [3.8], 'x_sn': [3.2] } # the 0.4 is just for display purposes (text alignment) def changed_value(self, ref, param, unit): new_val = self.refs[ref].value if unit is not None: new_val = new_val * unit if ref == "exp_slider": new_val = pre_encode(new_val.to(u.s)) elif ref == "age_slider": new_val = pre_encode(u.Dex(10.**new_val * u.Gyr)) elif ref == "metallicity_slider": new_val = pre_encode(u.Dex(new_val)) else: new_val = pre_encode(new_val) if ref == "crowding_slider": print(param, getattr(self, param), new_val) if getattr(self, param) != new_val: setattr(self, param, new_val) return True return False def controller(self, attr, old, new): """ Master controller callback. Instead of having a bunch of different callbacks, instead we're going to track which things have changed, and then call the individual update methods that are required. """ #Grab values from the inputs, tracking which have changed params = { 'exp': ('exp_slider', 'exptime', u.hour), 'age': ('age_slider', 'age', None), 'snr': ('snr_slider', 'snratio', u.electron**0.5), 'crowd': ('crowding_slider', 'crowding', u.dimensionless_unscaled), # u.ABmag / u.arcsec**2), 'dist': ('distance_slider', 'distance', u.Mpc), 'metal': ('metallicity_slider', 'metallicity', None), 'noise': ('noise_slider', 'noise', u.dimensionless_unscaled), 'aper': ('ap_slider', 'aperture', u.m) } updated = set( [par for par, args in params.items() if self.changed_value(*args)]) #Call the requisite update methods if not updated.isdisjoint({'dist', 'aper', 'exp'}): self.exposure_update() if not updated.isdisjoint({'dist', 'aper', 'exp', 'crowd'}): self.crowding_slider_update() if not updated.isdisjoint({'dist', 'aper', 'exp', 'snr'}): self.sn_slider_update() if 'noise' in updated: self.noise_slider_update() if 'dist' in updated: self.distance_slider_update() if 'metal' in updated: self.metallicity_slider_update() if 'age' in updated: self.age_slider_update() #Now, all of the plot updates should be handled by stream events, I think... #NONE OF THESE MATTER UNTIL SAVE/LOAD IS ADDED def update_toggle(self, active): if active: self.refs["user_prefix"].value = self.user_prefix self.refs["user_prefix"].disabled = True self.refs["save_button"].disabled = False self.overwrite_save = True else: self.refs["user_prefix"].disabled = False self.refs["save_button"].disabled = False self.overwrite_save = False #Save and Load def save(self): """ Save the current calculations. """ #Check for an existing save file if we're overwriting if self.overwrite_save and self.current_savefile: self.current_savefile = self.save_file(self.current_savefile, overwrite=True) else: #Set the user prefix from the bokeh interface prefix = self.refs["user_prefix"].value if not prefix.isalpha() or len(prefix) < 3: self.refs["save_message"].text = "Please include a prefix of at "\ "least 3 letters (and no other characters)." return self.user_prefix = prefix #Save the file: self.current_savefile = self.save_file() #Tell the user the filename or the error message. if not self.current_savefile: self.refs["save_message"].text = "Save unsuccessful; please " \ "contact the administrators." return self.refs["save_message"].text = "This calculation was saved with " \ "the ID {}".format(self.current_savefile) self.refs["update_save"].disabled = False def load(self): # Get the filename from the bokeh interface calcid = self.refs["load_filename"].value #Load the file code = self.load_file(calcid) if not code: #everything went fine #Update the interface self.refs["update_save"].disabled = False self.current_save = calcid self.refs["exp_slider"].value = pre_decode(self.exptime).value self.refs["mag_slider"].value = pre_decode( self.renorm_magnitude).value self.refs["ap_slider"].value = pre_decode(self.aperture).value self.refs["snr_slider"].value = pre_decode(self.snratio).value temp = self.templates.index(self.spectrum_type) self.refs["template_select"].value = self.template_options[temp] self.controller(None, None, None) errmsg = [ "Calculation ID {} loaded successfully.".format(calcid), "Calculation ID {} does not exist, please try again.".format( calcid), "Load unsuccessful; please contact the administrators.", "There was an error restoring the save state; please contact" " the administrators." ][code] if code == 0 and self.load_mismatch: errmsg += "<br><br><b><i>Load Mismatch Warning:</b></i> "\ "The saved model parameters did not match the " \ "parameter values saved for this tool. Saved " \ "model values were given priority." self.refs["load_message"].text = errmsg