Ejemplo n.º 1
0
    def __init__(self, config):

        self.wl, self.fwhm = load_wavelen(config['wavelength_file'])
        self.n_chan = len(self.wl)

        if 'auto_rebuild' in config:
            self.auto_rebuild = config['auto_rebuild']
        else:
            self.auto_rebuild = True
        self.lut_grid = config['lut_grid']
        self.lut_dir = config['lut_path']
        self.statevec = list(config['statevector'].keys())
        self.bvec = list(config['unknowns'].keys())
        self.n_point = len(self.lut_grid)
        self.n_state = len(self.statevec)

        # Retrieved variables.  We establish scaling, bounds, and
        # initial guesses for each state vector element.  The state
        # vector elements are all free parameters in the RT lookup table,
        # and they all have associated dimensions in the LUT grid.
        self.bounds, self.scale, self.init = [], [], []
        self.prior_mean, self.prior_sigma = [], []
        for key in self.statevec:
            element = config['statevector'][key]
            self.bounds.append(element['bounds'])
            self.scale.append(element['scale'])
            self.init.append(element['init'])
            self.prior_sigma.append(element['prior_sigma'])
            self.prior_mean.append(element['prior_mean'])
        self.bounds = s.array(self.bounds)
        self.scale = s.array(self.scale)
        self.init = s.array(self.init)
        self.prior_mean = s.array(self.prior_mean)
        self.prior_sigma = s.array(self.prior_sigma)
        self.bval = s.array([config['unknowns'][k] for k in self.bvec])
Ejemplo n.º 2
0
    def __init__(self, config):

        Surface.__init__(self, config)
        self.wl, fwhm = load_wavelen(config['wavelength_file'])
        self.statevec, self.bounds, self.scale, self.init = [], [], [], []

        # Each channel maps to a nonnegative absorption residual
        if 'absorption_resid_file' in config:
            abs_file = config['absorption_resid_file']
            self.C = loadmat(abs_file)['C']
            self.abs_inds = s.arange(len(self.wl))
            amin, amax = -1.0, 1.0
            self.statevec.extend(['ABS_%04i' % int(w) for w in self.wl])
            self.bounds.extend([[amin, amax] for w in self.wl])
            self.scale.extend([0.01 for w in self.wl])
            self.init.extend([0 for v in self.wl])
            ind_start = len(self.statevec)
        else:
            self.abs_inds = []
            self.C = s.array([[]], dtype=float)
            ind_start = 0

        # Other retrieved variables
        nonabs_sv = ['X', 'G', 'P', 'Y', 'GLINT', 'FLH']
        self.statevec.extend(nonabs_sv)
        self.nonabs_inds = ind_start + s.arange(len(nonabs_sv), dtype=int)
        self.X_ind = ind_start
        self.G_ind = ind_start+1
        self.P_ind = ind_start+2
        self.Y_ind = ind_start+3
        self.glint_ind = ind_start+4
        self.flh_ind = ind_start+5
        self.scale.extend([0.1, 1.0, 1.0, 1.0, 1.0, 1.0])
        self.init.extend([0.1, 0.1, 0.1, 0.1, 0.1, 0.01])
        self.bounds.extend(
            [[0, 1.0], [0, 1.0], [0, 10], [0, 2.5], [0, 1], [0, 10]])

        aw_wl, aw, q, q2 = s.loadtxt(config['h2oabs_file'], comments='#').T
        self.aw = interp1d(aw_wl, aw, fill_value='extrapolate')(self.wl)
        bbw_wl, bbw, q = s.loadtxt(config['h2oscatter_file'], comments='#').T
        self.bbw = interp1d(bbw_wl, bbw, fill_value='extrapolate')(self.wl)
        ap_wl, ap1, ap2 = s.loadtxt(config['pigments_file'], comments='#').T
        aphi = [interp1d(ap_wl, ap1, fill_value='extrapolate')(self.wl),
                interp1d(ap_wl, ap2, fill_value='extrapolate')(self.wl)]
        self.aphi_coeffs = s.array(aphi).T

        self.g0 = config.get('G0', 0.0895)  # Lee's first constant
        self.g1 = config.get('G1', 0.1247)  # Lee's second constant
        self.b1000 = s.argmin(abs(self.wl-1000))
        self.b900 = s.argmin(abs(900-self.wl))
        self.b440 = s.argmin(abs(self.wl-440))
        self.b490 = s.argmin(abs(self.wl-490))
        self.b550 = s.argmin(abs(self.wl-550))
        self.b640 = s.argmin(abs(self.wl-640))

        # Phytoplankton fluorescence peak center and width
        self.fl_mu = config.get('fl_mu', 683.0)
        self.fl_sigma = config.get('fl_fwhm', 25.0)/2.355
Ejemplo n.º 3
0
    def __init__(self, config):

        TabularRT.__init__(self, config)
        self.sixs_dir = self.find_basedir(config)
        self.wl, self.fwhm = load_wavelen(config['wavelength_file'])
        self.sixs_grid_init = s.arange(self.wl[0], self.wl[-1] + 2.5, 2.5)
        self.sixs_ngrid_init = len(self.sixs_grid_init)
        self.sixs_dir = self.find_basedir(config)
        self.params = {
            'aermodel': 1,
            'AOT550': 0.01,
            'H2OSTR': 0,
            'O3': 0.40,
            'day': config['day'],
            'month': config['month'],
            'elev': config['elev'],
            'alt': config['alt'],
            'atm_file': None,
            'abscf_data_directory': None,
            'wlinf': self.sixs_grid_init[0] / 1000.0,  # convert to nm
            'wlsup': self.sixs_grid_init[-1] / 1000.0
        }

        if 'obs_file' in config:
            # A special case where we load the observation geometry
            # from a custom-crafted text file
            g = Geometry(obs=config['obs_file'])
            self.params['solzen'] = g.solar_zenith
            self.params['solaz'] = g.solar_azimuth
            self.params['viewzen'] = g.observer_zenith
            self.params['viewaz'] = g.observer_azimuth
        else:
            # We have to get geometry from somewhere, so we presume it is
            # in the configuration file.
            for f in ['solzen', 'viewzen', 'solaz', 'viewaz']:
                self.params[f] = config[f]

        self.esd = s.loadtxt(config['earth_sun_distance_file'])
        dt = datetime(2000, self.params['month'], self.params['day'])
        self.day_of_year = dt.timetuple().tm_yday
        self.irr_factor = self.esd[self.day_of_year - 1, 1]

        irr = s.loadtxt(config['irradiance_file'], comments='#')
        self.iwl, self.irr = irr.T
        self.irr = self.irr / 10.0  # convert, uW/nm/cm2
        self.irr = self.irr / self.irr_factor**2  # consider solar distance

        self.build_lut()
Ejemplo n.º 4
0
    def __init__(self, config):

        self.statevec = []
        self.bounds = s.array([])
        self.scale = s.array([])
        self.init = s.array([])
        self.bvec = []
        self.bval = s.array([])
        self.emissive = False
        self.reconfigure(config)
        if 'reflectance_file' in config:
            self.rfl, self.wl = load_spectrum(config['reflectance_file'])
            self.n_wl = len(self.wl)
        elif 'wavelength_file' in config:
            self.wl, self.fwhm = load_wavelen(config['wavelength_file'])
            self.n_wl = len(self.wl)
Ejemplo n.º 5
0
    def __init__(self, config):
        """A model of the spectrometer instrument, including spectral 
        response and noise covariance matrices. Noise is typically calculated
        from a parametric model, fit for the specific instrument.  It is a 
        function of the radiance level."""

        # If needed, skip first index column and/or convert to nanometers
        self.wl_init, self.fwhm_init = load_wavelen(config['wavelength_file'])
        self.n_chan = len(self.wl_init)
        self.bounds = []
        self.scale = []
        self.statevec = []
        self.init = []
        self.prior_sigma = []
        self.prior_mean = []
        self.fast_resample = True

        # The "fast resample" option approximates a complete resampling by a
        # convolution with a uniform FWHM.
        if 'fast_resample' in config:
            self.fast_resample = config['fast_resample']

        # Are there free parameters?
        if 'statevector' in config:
            for key in config['statevector']:
                self.statevec.append(key)
                for attr in config['statevector'][key]:
                    getattr(self,
                            attr).append(config['statevector'][key][attr])
        self.prior_sigma = s.array(self.prior_sigma)
        self.prior_mean = s.array(self.prior_mean)
        self.n_state = len(self.statevec)

        # Number of integrations comprising the measurement.  Noise diminishes
        # with the square root of this number.
        self.integrations = config['integrations']

        if 'SNR' in config:

            # We have several ways to define the instrument noise.  The
            # simplest model is based on a single uniform SNR number that
            # is signal-independnet and applied uniformly to all wavelengths
            self.model_type = 'SNR'
            self.snr = float(config['SNR'])

        elif 'parametric_noise_file' in config:

            # The second option is a parametric, signal- and wavelength-
            # dependent noise function. This is given by a four-column
            # ASCII Text file.  Rows represent, respectively, the reference
            # wavelength, and coefficients A, B, and C that define the
            # noise-equivalent radiance via NeDL = A * sqrt(B+L) + C
            # For the actual radiance L.
            self.noise_file = config['parametric_noise_file']
            self.model_type = 'parametric'
            coeffs = s.loadtxt(self.noise_file, delimiter=' ', comments='#')
            p_a, p_b, p_c = [
                interp1d(coeffs[:, 0],
                         coeffs[:, col],
                         fill_value='extrapolate') for col in (1, 2, 3)
            ]
            self.noise = s.array([[p_a(w), p_b(w), p_c(w)]
                                  for w in self.wl_init])

        elif 'pushbroom_noise_file' in config:
            # The third option is a full pushbroom noise model that
            # specifies noise columns and covariances independently for
            # each cross-track location via an ENVI-format binary data file.
            self.model_type = 'pushbroom'
            self.noise_file = config['pushbroom_noise_file']
            D = loadmat(self.noise_file)
            self.ncols = D['columns'][0, 0]
            if self.n_chan != s.sqrt(D['bands'][0, 0]):
                logging.error('Noise model mismatches wavelength # bands')
                raise ValueError('Noise model mismatches wavelength # bands')
            cshape = ((self.ncols, self.n_chan, self.n_chan))
            self.covs = D['covariances'].reshape(cshape)

        else:
            logging.error('Instrument noise not defined.')
            raise IndexError('Please define the instrument noise.')

        # We track several unretrieved free variables, that are specified
        # in a fixed order (always start with relative radiometric
        # calibration)
        self.bvec = ['Cal_Relative_%04i' % int(w) for w in self.wl_init] + \
            ['Cal_Spectral', 'Cal_Stray_SRF']
        self.bval = s.zeros(self.n_chan + 2)

        if 'unknowns' in config:

            # First we take care of radiometric uncertainties, which add
            # in quadrature.  We sum their squared values.  Systematic
            # radiometric uncertainties account for differences in sampling
            # and radiative transfer that manifest predictably as a function
            # of wavelength.
            unknowns = config['unknowns']
            if 'channelized_radiometric_uncertainty_file' in unknowns:
                f = unknowns['channelized_radiometric_uncertainty_file']
                u = s.loadtxt(f, comments='#')
                if (len(u.shape) > 0 and u.shape[1] > 1):
                    u = u[:, 1]
                self.bval[:self.n_chan] = self.bval[:self.n_chan] + pow(u, 2)

            # Uncorrelated radiometric uncertainties are consistent and
            # independent in all channels.
            if 'uncorrelated_radiometric_uncertainty' in unknowns:
                u = unknowns['uncorrelated_radiometric_uncertainty']
                self.bval[:self.n_chan] = self.bval[:self.n_chan] + \
                    pow(s.ones(self.n_chan) * u, 2)

            # Radiometric uncertainties combine via Root Sum Square...
            # Be careful to avoid square roots of zero!
            small = s.ones(self.n_chan) * eps
            self.bval[:self.n_chan] = s.maximum(self.bval[:self.n_chan], small)
            self.bval[:self.n_chan] = s.sqrt(self.bval[:self.n_chan])

            # Now handle spectral calibration uncertainties
            if 'wavelength_calibration_uncertainty' in unknowns:
                self.bval[-2] = unknowns['wavelength_calibration_uncertainty']
            if 'stray_srf_uncertainty' in unknowns:
                self.bval[-1] = unknowns['stray_srf_uncertainty']

        # Determine whether the calibration is fixed.  If it is fixed,
        # and the wavelengths of radiative transfer modeling and instrument
        # are the same, then we can bypass compputationally expensive sampling
        # operations later.
        self.calibration_fixed = (not ('FWHM_SCL' in self.statevec)) and \
            (not ('WL_SHIFT' in self.statevec)) and \
            (not ('WL_SPACE' in self.statevec))