Example #1
0
    def calibrate_rad_to_bt(self, radiance, key):
        """IR channel calibration."""
        # using the method from PUG section Converting from Effective Radiance to Brightness Temperature for IR Channels

        measured = self.get_channel_measured_group_path(key['name'])

        vc = self[measured +
                  "/radiance_to_bt_conversion_coefficient_wavenumber"]

        a = self[measured + "/radiance_to_bt_conversion_coefficient_a"]
        b = self[measured + "/radiance_to_bt_conversion_coefficient_b"]

        c1 = self[measured + "/radiance_to_bt_conversion_constant_c1"]
        c2 = self[measured + "/radiance_to_bt_conversion_constant_c2"]

        for v in (vc, a, b, c1, c2):
            if v == v.attrs.get("FillValue",
                                default_fillvals.get(v.dtype.str[1:])):
                logger.error("{:s} set to fill value, cannot produce "
                             "brightness temperatures for {:s}.".format(
                                 v.attrs.get(
                                     "long_name",
                                     "at least one necessary coefficient"),
                                 measured))
                return radiance * np.nan

        nom = c2 * vc
        denom = a * np.log(1 + (c1 * vc**3) / radiance)

        res = nom / denom - b / a
        return res
Example #2
0
    def _ir_calibrate(self, radiance, measured, root):
        """IR channel calibration."""
        coef = self[measured + "/radiance_unit_conversion_coefficient"]
        wl_c = self[root + "/central_wavelength_actual"]

        a = self[measured + "/radiance_to_bt_conversion_coefficient_a"]
        b = self[measured + "/radiance_to_bt_conversion_coefficient_b"]

        c1 = self[measured + "/radiance_to_bt_conversion_constant_c1"]
        c2 = self[measured + "/radiance_to_bt_conversion_constant_c2"]

        for v in (coef, wl_c, a, b, c1, c2):
            if v == v.attrs.get("FillValue",
                                default_fillvals.get(v.dtype.str[1:])):
                logger.error("{:s} set to fill value, cannot produce "
                             "brightness temperatures for {:s}.".format(
                                 v.attrs.get(
                                     "long_name",
                                     "at least one necessary coefficient"),
                                 root))
                return xr.DataArray(da.full(shape=radiance.shape,
                                            chunks=radiance.chunks,
                                            fill_value=np.nan),
                                    dims=radiance.dims,
                                    coords=radiance.coords,
                                    attrs=radiance.attrs)

        Lv = radiance * coef
        vc = 1e6 / wl_c  # from wl in um to wn in m^-1
        nom = c2 * vc
        denom = a * np.log(1 + (c1 * vc**3) / Lv)

        res = nom / denom - b / a
        res.attrs["units"] = "K"
        return res
Example #3
0
    def calibrate_rad_to_refl(self, radiance, key):
        """VIS channel calibration."""
        measured = self.get_channel_measured_group_path(key['name'])

        cesi = self[measured + "/channel_effective_solar_irradiance"]

        if cesi == cesi.attrs.get("FillValue",
                                  default_fillvals.get(cesi.dtype.str[1:])):
            logger.error(
                "channel effective solar irradiance set to fill value, "
                "cannot produce reflectance for {:s}.".format(measured))
            return radiance * np.nan

        sun_earth_distance = np.mean(
            self["state/celestial/earth_sun_distance"]) / 149597870.7  # [AU]

        # TODO remove this check when old versions of IDPF test data (<v5) are deprecated.
        if sun_earth_distance < 0.9 or sun_earth_distance > 1.1:
            logger.info(
                'The variable state/celestial/earth_sun_distance contains unexpected values'
                '(mean value is {} AU). Defaulting to 1 AU for reflectance calculation.'
                ''.format(sun_earth_distance))
            sun_earth_distance = 1

        res = 100 * radiance * np.pi * sun_earth_distance**2 / cesi
        return res
Example #4
0
    def _get_aux_data_lut_vector(self, aux_data_name):
        """Load the lut vector of an auxiliary variable."""
        lut = self[AUX_DATA[aux_data_name]]

        fv = default_fillvals.get(lut.dtype.str[1:], np.nan)
        lut = lut.where(lut != fv)

        return lut
Example #5
0
    def get_dataset(self, key, info=None):
        """Load a dataset."""

        logger.debug('Reading {} from {}'.format(key.name, self.filename))
        # Get the dataset
        # Get metadata for given dataset
        measured, root = self.get_channel_dataset(key.name)
        radlab = measured + "/effective_radiance"
        data = self[radlab]

        attrs = data.attrs.copy()
        info = info.copy()
        fv = attrs.pop("FillValue",
                       default_fillvals.get(data.dtype.str[1:], np.nan))
        vr = attrs.pop("valid_range", [-np.inf, np.inf])
        if key.calibration == "counts":
            attrs["_FillValue"] = fv
            nfv = fv
        else:
            nfv = np.nan
        data = data.where(data >= vr[0], nfv)
        data = data.where(data <= vr[1], nfv)
        if key.calibration == "counts":
            # from package description, this just means not applying add_offset
            # and scale_factor
            attrs.pop("scale_factor")
            attrs.pop("add_offset")
            data.attrs["units"] = "1"
            res = data
        else:
            data = (data * attrs.pop("scale_factor", 1) +
                    attrs.pop("add_offset", 0))

            if key.calibration in ("brightness_temperature", "reflectance"):
                res = self.calibrate(data, key, measured, root)
            else:
                res = data
                data.attrs["units"] = attrs["units"]
        # pre-calibration units no longer apply
        info.pop("units")
        attrs.pop("units")

        self.nlines, self.ncols = res.shape
        res.attrs.update(key.to_dict())
        res.attrs.update(info)
        res.attrs.update(attrs)
        return res
Example #6
0
    def get_dataset(self, key, info=None):
        """Load a dataset."""
        logger.debug('Reading {} from {}'.format(key.name, self.filename))
        # Get the dataset
        # Get metadata for given dataset
        measured = self.get_channel_measured_group_path(key.name)
        data = self[measured + "/effective_radiance"]

        attrs = data.attrs.copy()
        info = info.copy()

        fv = attrs.pop("FillValue",
                       default_fillvals.get(data.dtype.str[1:], np.nan))
        vr = attrs.get("valid_range", [-np.inf, np.inf])
        if key.calibration == "counts":
            attrs["_FillValue"] = fv
            nfv = fv
        else:
            nfv = np.nan
        data = data.where(data >= vr[0], nfv)
        data = data.where(data <= vr[1], nfv)

        res = self.calibrate(data, key)

        # pre-calibration units no longer apply
        info.pop("units")
        attrs.pop("units")

        res.attrs.update(key.to_dict())
        res.attrs.update(info)
        res.attrs.update(attrs)

        res.attrs["platform_name"] = self._platform_name_translate.get(
            self["/attr/platform"], self["/attr/platform"])

        # remove unpacking parameters for calibrated data
        if key.calibration in ['brightness_temperature', 'reflectance']:
            res.attrs.pop("add_offset")
            res.attrs.pop("warm_add_offset")
            res.attrs.pop("scale_factor")
            res.attrs.pop("warm_scale_factor")

        # remove attributes from original file which don't apply anymore
        res.attrs.pop('long_name')

        return res
Example #7
0
    def calibrate_rad_to_refl(self, radiance, key):
        """VIS channel calibration."""
        measured = self.get_channel_measured_group_path(key['name'])

        cesi = self[measured + "/channel_effective_solar_irradiance"]

        if cesi == cesi.attrs.get("FillValue",
                                  default_fillvals.get(cesi.dtype.str[1:])):
            logger.error(
                "channel effective solar irradiance set to fill value, "
                "cannot produce reflectance for {:s}.".format(measured))
            return radiance * np.nan

        sun_earth_distance = np.mean(
            self["state/celestial/earth_sun_distance"]) / 149597870.7  # [AU]

        res = 100 * radiance * np.pi * sun_earth_distance**2 / cesi
        return res
Example #8
0
    def _vis_calibrate(self, radiance, measured):
        """VIS channel calibration."""
        # radiance to reflectance taken as in mipp/xrit/MSG.py
        # again FCI User Guide is not clear on how to do this

        cesilab = measured + "/channel_effective_solar_irradiance"
        cesi = self[cesilab]
        if cesi == cesi.attrs.get("FillValue",
                                  default_fillvals.get(cesi.dtype.str[1:])):
            logger.error(
                "channel effective solar irradiance set to fill value, "
                "cannot produce reflectance for {:s}.".format(measured))
            return xr.DataArray(da.full(shape=radiance.shape,
                                        chunks=radiance.chunks,
                                        fill_value=np.nan),
                                dims=radiance.dims,
                                coords=radiance.coords,
                                attrs=radiance.attrs)

        sirr = float(cesi)
        res = radiance / sirr * 100
        res.attrs["units"] = "%"
        return res
Example #9
0
    def _get_dataset_measurand(self, key, info=None):
        """Load dataset corresponding to channel measurement.

        Load a dataset when the key refers to a measurand, whether uncalibrated
        (counts) or calibrated in terms of brightness temperature, radiance, or
        reflectance.
        """
        # Get the dataset
        # Get metadata for given dataset
        measured = self.get_channel_measured_group_path(key['name'])
        data = self[measured + "/effective_radiance"]

        attrs = data.attrs.copy()
        info = info.copy()

        fv = attrs.pop("FillValue",
                       default_fillvals.get(data.dtype.str[1:], np.nan))
        vr = attrs.get("valid_range", [-np.inf, np.inf])
        if key['calibration'] == "counts":
            attrs["_FillValue"] = fv
            nfv = fv
        else:
            nfv = np.nan
        data = data.where(data >= vr[0], nfv)
        data = data.where(data <= vr[1], nfv)

        res = self.calibrate(data, key)

        # pre-calibration units no longer apply
        attrs.pop("units")

        # For each channel, the effective_radiance contains in the
        # "ancillary_variables" attribute the value "pixel_quality".  In
        # FileYAMLReader._load_ancillary_variables, satpy will try to load
        # "pixel_quality" but is lacking the context from what group to load
        # it: in the FCI format, each channel group (data/<channel>/measured) has
        # its own data variable 'pixel_quality'.
        # Until we can have multiple pixel_quality variables defined (for
        # example, with https://github.com/pytroll/satpy/pull/1088), rewrite
        # the ancillary variable to include the channel. See also
        # https://github.com/pytroll/satpy/issues/1171.
        if "pixel_quality" in attrs["ancillary_variables"]:
            attrs["ancillary_variables"] = attrs[
                "ancillary_variables"].replace("pixel_quality",
                                               key['name'] + "_pixel_quality")
        else:
            raise ValueError(
                "Unexpected value for attribute ancillary_variables, "
                "which the FCI file handler intends to rewrite (see "
                "https://github.com/pytroll/satpy/issues/1171 for why). "
                f"Expected 'pixel_quality', got {attrs['ancillary_variables']:s}"
            )

        res.attrs.update(key.to_dict())
        res.attrs.update(info)
        res.attrs.update(attrs)

        res.attrs["platform_name"] = self._platform_name_translate.get(
            self["/attr/platform"], self["/attr/platform"])

        # remove unpacking parameters for calibrated data
        if key['calibration'] in ['brightness_temperature', 'reflectance']:
            res.attrs.pop("add_offset")
            res.attrs.pop("warm_add_offset")
            res.attrs.pop("scale_factor")
            res.attrs.pop("warm_scale_factor")

        # remove attributes from original file which don't apply anymore
        res.attrs.pop('long_name')

        return res