Beispiel #1
0
 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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
 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)
Beispiel #5
0
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]))
Beispiel #6
0
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.")
    
    """
Beispiel #7
0
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))
Beispiel #8
0
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
Beispiel #9
0
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'))
Beispiel #10
0
                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={
Beispiel #11
0
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)
Beispiel #12
0
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