Example #1
0
class VariableHolder(ParamHolder):
    """ Allows parameter values to be interpolated using a frequency repsonse file"""

    fname = Property(dtype=str, default=None, help="Data file")
    var_type = Choice(choices=['pdf', 'dist', 'gauss', 'const'],
                      default='const')

    def __init__(self, *args, **kwargs):
        """ Constuctor """
        self._value = None
        super(VariableHolder, self).__init__(*args, **kwargs)
        self._sampled_values = None
        self._cached_interps = None

    @staticmethod
    def _channel_value(arr, chan_idx):
        """ Picks the value for a paricular channel,

        This allows using a single parameter to represent multiple channels
        """
        if not isinstance(arr, Iterable):
            return arr
        if not arr.shape:
            return arr
        if len(arr) < 2:
            return arr[0]
        return arr[chan_idx]

    def _cache_interps(self, freqs=None):
        """ Cache the interpolator object """
        if self.var_type == 'const':
            self._cached_interps = None
            return
        if self.var_type == 'gauss':
            if np.isnan(self.errors).any():
                self._cached_interps = None
                return

            self._cached_interps = np.array([
                sps.norm(loc=val_, scale=sca_) for val_, sca_ in zip(
                    np.atleast_1d(self.value), np.atleast_1d(self.errors))
            ])
            return
        tokens = self.fname.split(',')
        if self.var_type == 'pdf':
            self._cached_interps = np.array(
                [ChoiceDist(cfg_path(token)) for token in tokens])
            self._value = np.array(
                [pdf.mean() for pdf in self._cached_interps])
            return
        self._cached_interps = np.array(
            [FreqInterp(cfg_path(token)) for token in tokens])
        if freqs is None:
            self._value = np.array(
                [pdf.mean_trans() for pdf in self._cached_interps])
            return
        self._value = np.array(
            [pdf.rvs(freqs) for pdf in self._cached_interps])

    def rvs(self, nsamples, freqs=None, chan_idx=0):
        """ Sample values

        This just returns the sampled values.
        It does not store them.
        """
        self._cache_interps(freqs)
        val = self._channel_value(self.value, chan_idx)
        if self._cached_interps is None or not nsamples:
            return val
        interp = self._channel_value(self._cached_interps, chan_idx)
        if self.var_type == 'gauss':
            return interp.rvs(nsamples).reshape((nsamples, 1))
        if self.var_type == 'pdf':
            return interp.rvs(nsamples).reshape((nsamples, 1))
        return interp.rvs(freqs, nsamples).reshape((nsamples, len(freqs)))

    def sample(self, nsamples, freqs=None, chan_idx=0):
        """ Sample values

        This stores the sampled values.
        """
        self._sampled_values = self.rvs(nsamples, freqs, chan_idx)
        return self.SI

    def unsample(self):
        """ This removes the stored sampled values"""
        self._sampled_values = None

    @property
    def scaled(self):
        """Return the product of the value and the scale

        This uses the stored sampled values if they are present.
        """
        if self._sampled_values is not None:
            return self._sampled_values * self.scale
        return super(VariableHolder, self).scaled
Example #2
0
 class TestClass(Model):
     v = Property(dtype=dtype, default=default, help="A Property")
     v2 = Property(dtype=dtype, help="A Property")
     v3 = Property(dtype=dtype, required=True, help="A Property")
     v4 = Property(dtype=dtype, default=test_val, help="A Property")
Example #3
0
class Atmosphere(Model):
    """ Atmosphere model """
    atm_model_file = Property(dtype=str)

    def __init__(self, **kwargs):
        """ Constructor """
        self._atm_model = None
        self._telescope = None
        self._sampled_keys = None
        self._nsamples = 1
        super(Atmosphere, self).__init__(**kwargs)

    def set_telescope(self, value):
        """ Set the telescope

        This is needed to sample elevation and PWV values
        """
        self._telescope = value

    @cached(uses=[atm_model_file])
    def cached_model(self):
        """ Cache the Atmosphere model """
        if is_not_none(self._telescope.custom_atm_file):
            return CustomAtm(cfg_path(self._telescope.custom_atm_file))
        if is_not_none(self.atm_model_file):
            return AtmModel(cfg_path(self.atm_model_file),
                            self._telescope.site)
        return None

    def sample(self, nsamples):
        """ Sample the atmosphere """
        model = self.cached_model
        if isinstance(model, CustomAtm):
            self._sampled_keys = None
            self._nsamples = nsamples
            return
        self._telescope.pwv.sample(nsamples)
        self._telescope.elevation.sample(nsamples)
        self._sampled_keys = model.get_keys(
            1e-6 * np.atleast_1d(self._telescope.pwv()),
            np.atleast_1d(self._telescope.elevation()))  #pylint: disable=no-member
        self._nsamples = max(nsamples, 1)

    def temp(self, freqs):
        """ Get sampled temperatures """
        model = self.cached_model
        nfreqs = len(freqs)
        out_shape = (max(self._nsamples, 1), 1, nfreqs)
        if self._sampled_keys is None:
            ones = np.ones((max(self._nsamples, 1), 1, 1))
            return (ones * model.temp(freqs)).reshape(out_shape)  #pylint: disable=no-member
            #out_shape = (1, 1, nfreqs)
            #return model.temp(freqs).reshape(out_shape)  #pylint: disable=no-member
        return model.temp(self._sampled_keys, freqs).reshape(out_shape)  #pylint: disable=no-member

    def trans(self, freqs):
        """ Get sampled transmission coefs """
        model = self.cached_model
        nfreqs = len(freqs)
        out_shape = (max(self._nsamples, 1), 1, nfreqs)
        if self._sampled_keys is None:
            ones = np.ones((max(self._nsamples, 1), 1, 1))
            return (ones * model.trans(freqs)).reshape(out_shape)  #pylint: disable=no-member
            #out_shape = (1, 1, nfreqs)
            #return model.trans(freqs).reshape(out_shape)  #pylint: disable=no-member
        return model.trans(self._sampled_keys, freqs).reshape(out_shape)  #pylint: disable=no-member
Example #4
0
 class TestClass(Model):
     vv = Property()
Example #5
0
 class TestClass(Model):
     vv = Property(dummy=3)
Example #6
0
    class OtherClass(Model):

        a = Property(dtype=float, default=3., help='variable a')
        x = Ref()
        z = Ref(attr='z')
        der = Ref(attr='der')
Example #7
0
class Channel(Model):  #pylint: disable=too-many-instance-attributes
    """ Channel Model """

    _min_tc_tb_diff = 0.010

    band_center = Variable(unit="GHz")
    fractional_bandwidth = Variable(default=.35)
    band_response = Variable(default=1.)

    det_eff = Variable(default=1.)
    squid_nei = Variable(default=1., unit='pA/rtHz')
    bolo_resistance = Variable(default=1., unit='Ohm')

    pixel_size = Variable(default=6.8, unit='mm')
    waist_factor = Variable(default=3.)

    Tc = Variable(default=.165, unit='K')
    Tc_fraction = Variable()

    num_det_per_water = Property(dtype=int, default=542)
    num_wafer_per_optics_tube = Property(dtype=int, default=1)
    num_optics_tube = Property(dtype=int, default=3)

    psat = Variable()
    psat_factor = Variable(default=3.)

    read_frac = Variable(default=0.1)
    carrier_index = Variable(default=3)
    G = Variable(unit='pW/K')
    Flink = Variable()
    Yield = Variable()
    response_factor = Variable()
    nyquist_inductance = Variable()

    noise_calc = noise.Noise()

    def __init__(self, **kwargs):
        """ Constructor """
        self._optical_effic = None
        self._optical_emiss = None
        self._optical_temps = None
        self._sky_temp_dict = None
        self._sky_tran_dict = None
        self._det_effic = None
        self._det_emiss = None
        self._det_temp = None
        self._camera = None
        self._idx = None
        super(Channel, self).__init__(**kwargs)
        self._freqs = None
        self._flo = None
        self._fhi = None
        self._freq_mask = None
        self.bandwidth = None

    def set_camera(self, camera, idx):
        """ Set the parent camera and the channel index """
        self._camera = camera
        self._idx = idx

    def sample(self, nsamples):
        """ Sample PDF parameters """
        self.det_eff.sample(nsamples)
        self.squid_nei.sample(nsamples)
        self.bolo_resistance.sample(nsamples)

    @property
    def camera(self):
        """ Return the parent camera """
        return self._camera

    @property
    def freqs(self):
        """ Return the evaluation frequencies """
        return self._freqs

    @property
    def flo(self):
        """ Return the -3dB point"""
        return self._flo

    @property
    def fhi(self):
        """ Return the +3dB point """
        return self._fhi

    @property
    def ndet(self):
        """ Return the total number of detectors per channel """
        return self.num_det_per_water * self.num_wafer_per_optics_tube * self.num_optics_tube

    @property
    def idx(self):
        """ Return the channel index """
        return self._idx

    def photon_NEP(self, elem_power, elems=None, ap_names=None):
        """ Return the photon NEP given the power in the element in the optical chain """
        if elems is None:
            return self.noise_calc.photon_NEP(elem_power, self._freqs)
        det_pitch = self.pixel_size.SI / (self.camera.f_number.SI *
                                          physics.lamb(self.band_center.SI))
        return self.noise_calc.photon_NEP(elem_power,
                                          self._freqs,
                                          elems=elems,
                                          det_pitch=det_pitch,
                                          ap_names=ap_names)

    def bolo_Psat(self, opt_pow):
        """ Return the PSAT used the the computation """
        if is_not_none(self.psat_factor) and np.isfinite(self.psat_factor.SI):
            p_sat = opt_pow * self.psat_factor.SI
        else:
            p_sat = self.psat.SI
        return p_sat

    def bolo_G(self, opt_pow):
        """ Return the Bolometeric G factor used in the computation """
        tb = self._camera.bath_temperature()
        tc = self.Tc.SI
        n = self.carrier_index.SI
        if is_not_none(self.G) and np.isfinite(self.G.SI).all():
            g = self.G.SI
        else:
            if is_not_none(self.psat_factor) and np.isfinite(
                    self.psat_factor.SI):
                p_sat = opt_pow * self.psat_factor.SI
            else:
                p_sat = self.psat.SI
            g = noise.G(p_sat, n, tb, tc)
        return g

    def bolo_Flink(self):
        """ Return the Bolometeric f-link used in the computation """
        tb = self._camera.bath_temperature()
        tc = self.Tc.SI
        n = self.carrier_index.SI
        if is_not_none(self.Flink) and np.isfinite(self.Flink.SI):
            flink = self.Flink.SI
        else:
            flink = noise.Flink(n, tb, tc)
        return flink

    def bolo_NEP(self, opt_pow):
        """ Return the bolometric NEP given the detector details """
        tb = self._camera.bath_temperature()
        tc = self.Tc.SI
        n = self.carrier_index.SI  #1. #self.n
        if is_not_none(self.G) and np.isfinite(self.G.SI).all():
            g = self.G.SI
        else:
            if is_not_none(self.psat_factor) and np.isfinite(
                    self.psat_factor.SI):
                p_sat = opt_pow * self.psat_factor.SI
            else:
                p_sat = self.psat.SI
            g = noise.G(p_sat, n, tb, tc)
        if is_not_none(self.Flink) and np.isfinite(self.Flink.SI):
            flink = self.Flink.SI
        else:
            flink = noise.Flink(n, tb, tc)
        return noise.bolo_NEP(flink, g, tc)

    def read_NEP(self, opt_pow):
        """ Return the readout NEP given the detector details """
        if np.isnan(self.squid_nei.SI).any():
            return None
        if np.isnan(self.bolo_resistance.SI).any():
            return None
        if is_not_none(self.psat) and np.isfinite(self.psat.SI).all():
            p_stat = self.psat.SI
        else:
            p_stat = self.psat_factor.SI * opt_pow
        p_bias = (p_stat - opt_pow).clip(0, np.inf)
        if is_not_none(self.response_factor) and np.isfinite(
                self.response_factor.SI).all():
            s_fact = self.response_factor.SI
        else:
            s_fact = 1.
        return noise.read_NEP(p_bias, self.bolo_resistance.SI.T,
                              self.squid_nei.SI.T, s_fact)

    def compute_evaluation_freqs(self, freq_resol=None):
        """ Compute and return the evaluation frequencies """
        self.bandwidth = self.band_center.SI * self.fractional_bandwidth.SI
        if freq_resol is None:
            freq_resol = 0.05 * self.bandwidth
        else:
            freq_resol = freq_resol * 1e9
        self._flo = self.band_center.SI - 0.5 * self.bandwidth
        self._fhi = self.band_center.SI + 0.5 * self.bandwidth
        freq_step = np.ceil(self.bandwidth / freq_resol).astype(int)

        self._freqs = np.linspace(self._flo, self._fhi, freq_step + 1)
        band_mean_response = self.band_response.sample(0, self._freqs)
        if np.isscalar(band_mean_response):
            return self._freqs
        self._flo, self._fhi = physics.band_edges(self._freqs,
                                                  band_mean_response)
        self.bandwidth = self._fhi - self._flo
        freq_mask = np.bitwise_and(self._freqs >= self._flo,
                                   self._freqs <= self._fhi)
        self._freqs = self._freqs[freq_mask]
        return self._freqs

    def eval_optical_chain(self, nsample=0, freq_resol=None):
        """ Evaluate the performance of the optical chain for this channel """
        self.compute_evaluation_freqs(freq_resol)
        self._optical_effic = []
        self._optical_emiss = []
        self._optical_temps = []
        for elem in self._camera.optics.values():
            effic, emiss, temps = elem.compute_channel(self, self._freqs,
                                                       nsample)
            self._optical_effic.append(effic)
            self._optical_emiss.append(emiss)
            self._optical_temps.append(temps)

    def eval_det_response(self, nsample=0, freq_resol=None):
        """ Evaluate the detector response for this channel """
        self._freqs = self.compute_evaluation_freqs(freq_resol)
        self.band_response.sample(nsample, self._freqs)
        self.det_eff.sample(nsample)
        #def_eff_shaped = np.expand_dims(self.det_eff(), -1)
        self._det_effic = self.band_response.SI * self.det_eff.SI
        self._det_emiss = 0.
        self._det_temp = self._camera.bath_temperature()

    def eval_sky(self, universe, freq_resol=None):
        """ Evaluate the sky parameters for this channel

        This is done here, b/c the frequencies we care about are chanel dependent.
        """
        self._freqs = self.compute_evaluation_freqs(freq_resol)
        self._sky_temp_dict = universe.temp(self._freqs)
        self._sky_tran_dict = universe.trans(self._freqs)

    @property
    def optical_effic(self):
        """ Return the optical element efficiecies for this channel """
        return self._optical_effic

    @property
    def optical_emiss(self):
        """ Return the optical element emissivities for this channel """
        return self._optical_emiss

    @property
    def optical_temps(self):
        """ Return the optical element temperatures for this channel """
        return self._optical_temps

    @property
    def sky_names(self):
        """ Return the list of the names of the sky components """
        return list(self._sky_temp_dict.keys())

    @property
    def sky_temps(self):
        """ Return the sky component temperatures for this channel """
        return [self._sky_temp_dict.get(k) for k in Universe.sources]

    @property
    def sky_effic(self):
        """ Return the sky component efficiecies for this channel """
        return [self._sky_tran_dict.get(k, 1.) for k in Universe.sources]

    @property
    def sky_emiss(self):
        """ Return the sky component emissivities for this channel """
        return [1] * len(Universe.sources)
Example #8
0
class Instrument(Model):
    """ Class to represent an instrument """
    site = Property(dtype=str, required=True)
    sky_temp = Variable(required=True, unit='K')
    obs_time = Variable(required=True, unit='yr')
    sky_fraction = Variable(required=True)
    NET = Variable(required=True)

    custom_atm_file = Property(dtype=str)

    elevation = Variable(required=True)
    pwv = Variable(required=True)
    obs_effic = Variable(required=True)

    readout = Property(dtype=Readout, required=True)
    camera_config = Property(dtype=dict, required=True)
    optics_config = Property(dtype=dict, required=True)
    channel_default = Property(dtype=dict, required=True)

    def __init__(self, **kwargs):
        """ Constructor """
        super(Instrument, self).__init__(**kwargs)
        self.optics = build_optics_class(**self.optics_config)
        self.cameras = build_cameras(self.channel_default, self.camera_config)
        self._tables = None
        self._sns_dict = None
        for key, val in self.cameras.items():
            self.__dict__[key] = val
            val.set_parent(self, key)

    def eval_sky(self, universe, nsamples=0, freq_resol=None):
        """ Sample requested inputs and evaluate the parameters of the sky model """
        universe.sample(nsamples)
        self.obs_effic.sample(nsamples)
        for camera in self.cameras.values():
            camera.eval_sky(universe, freq_resol)

    def eval_instrument(self, nsamples=0, freq_resol=None):
        """ Sample requested inputs and evaluate the parameters of the instrument model """
        for camera in self.cameras.values():
            camera.sample(nsamples)
            camera.eval_optical_chains(nsamples, freq_resol)
            camera.eval_det_response(nsamples, freq_resol)

    def eval_sensitivities(self):
        """ Evaluate the sensitvities """
        self._sns_dict = odict()
        for cam_name, camera in self.cameras.items():
            for chan_name, channel in camera.channels.items():
                full_name = "%s%s" % (cam_name, chan_name)
                self._sns_dict[full_name] = Sensitivity(channel)

    def make_tables(self, basename="", **kwargs):
        """ Make fits tables with output values """
        self._tables = TableDict()
        for key, val in self._sns_dict.items():
            val.make_tables("%s%s" % (basename, key), self._tables, **kwargs)

        # get the summary tables and stack them
        if kwargs.get('save_summary', True):
            sum_keys = [ key for key in self._tables.keys() if key.find('_summary') > 0 ]
            sum_table = vstack([self._tables.pop_table(sum_key) for sum_key in sum_keys])
            self._tables.add_datatable("%ssummary" % basename, sum_table)
        # get the optical tables adn stack them
        if kwargs.get('save_optical', True):
            opt_keys = [ key for key in self._tables.keys() if key.find('_optical') > 0 ]
            opt_table = vstack([self._tables.pop_table(opt_key) for opt_key in opt_keys])
            self._tables.add_datatable("%soptical" % basename, opt_table)
        return self._tables

    def write_tables(self, filename):
        """ Write output fits tables """
        if self._tables:
            self._tables.save_datatables(filename)

    @property
    def tables(self):
        """ Get the out put data tables"""
        return self._tables

    def print_summary(self, stream=sys.stdout):
        """ Print summary stats in humman readable format """
        for key, val in self._sns_dict.items():
            stream.write("%s ---------\n" % key)
            val.print_summary(stream)
            stream.write("---------\n")

    def print_optical_output(self, stream=sys.stdout):
        """ Print summary stats in humman readable format """
        for key, val in self._sns_dict.items():
            stream.write("%s ---------\n" % key)
            val.print_optical_output(stream)
            stream.write("---------\n")

    def run(self, universe, sim_cfg, basename=""):
        """ Run the analysis chain """
        self.eval_sky(universe, sim_cfg.nsky_sim, sim_cfg.freq_resol)
        self.eval_instrument(sim_cfg.ndet_sim, sim_cfg.freq_resol)
        self.eval_sensitivities()
        save_summary = sim_cfg.save_summary
        if max(sim_cfg.nsky_sim, 1) * max(sim_cfg.ndet_sim, 1) == 1:
            save_summary = False
        self.make_tables(basename, save_summary=save_summary, save_sim=sim_cfg.save_sim, save_cfg=sim_cfg.save_optical)