コード例 #1
0
    def test_date_to_days(self):
        days = date_to_days(date(2019, 1, 1))
        self.assertEqual(days, 1)

        days = date_to_days(date(19, 1, 1))
        self.assertEqual(days, 1)

        days = date_to_days(date(19, 12, 31))
        self.assertEqual(days, 365)

        days = date_to_days(date(19, 3, 12))
        self.assertEqual(days, 71)
コード例 #2
0
    def get_calculation_inputs_between(
            self,
            start_date: date,
            end_date: date,
            brewer_id,
            settings: Settings,
            uvr_file: Optional[str] = None) -> List[CalculationInput]:
        """
        Create inputs for all UV Files found for between a start date and an end date for a given brewer id.

        :param start_date: the dates' lower bound (inclusive) for the measurements
        :param end_date: the dates' upper bound (inclusive) for the measurements
        :param brewer_id: the id of the brewer instrument
        :param settings: the settings to use for the calculation
        :param uvr_file: the uvr file to use for the calculation or None to use the default
        :return: the calculation inputs
        """

        if uvr_file is None and settings.uvr_data_source == DataSource.FILES and brewer_id in self._file_dict:
            uvr_file = self._file_dict[brewer_id].uvr_files[0].file_name

        input_list = []
        for d in date_range(start_date, end_date):
            year = d.year - 2000
            days = date_to_days(d)

            LOG.debug("Creating input for date %s as days %d and year %d",
                      d.isoformat(), days, year)
            calculation_input = self.input_from_files(f"{days:03}",
                                                      f"{year:02}", brewer_id,
                                                      settings, uvr_file)
            if calculation_input is not None:
                input_list.append(calculation_input)

        return input_list
コード例 #3
0
    def cloud_cover(self) -> CloudCover:
        position = self.uv_file_entries[0].header.position
        d = self.uv_file_entries[0].header.date

        days = date_to_days(d)
        parameter_value = self.parameters.cloud_cover(days)
        if parameter_value is not None:
            return ParameterCloudCover(parameter_value)
        else:
            return get_cloud_cover(position.latitude, position.longitude, d)
コード例 #4
0
ファイル: output.py プロジェクト: pec0ra/buvic
    def _get_single_result_content(self, result: Result) -> str:
        # Initialize the content that will be returned
        content = ""

        minutes = result.uv_file_entry.raw_values[0].time
        days = date_to_days(result.uv_file_entry.header.date)
        ozone = result.calculation_input.ozone.interpolated_ozone(minutes, result.calculation_input.settings.default_ozone)
        albedo = result.calculation_input.parameters.interpolated_albedo(days, result.calculation_input.settings.default_albedo)
        aerosol = result.calculation_input.parameters.interpolated_aerosol(days, result.calculation_input.settings.default_aerosol)
        cos_cor_to_apply = result.calculation_input.cos_correction_to_apply(minutes)

        # If the value comes from Darksky, we add the cloud cover in parenthesis after the coscor type
        cloud_cover_value = ""
        if cos_cor_to_apply != CosCorrection.NONE and isinstance(result.calculation_input.cloud_cover, DarkskyCloudCover):
            cloud_cover_value = f"(darksky:{result.calculation_input.cloud_cover.darksky_value(minutes)})"

        content += f"% Generated with Brewer UV Irradiance Calculation {APP_VERSION} at {datetime.now().replace(microsecond=0)}\n"
        content += f"% https://github.com/pec0ra/buvic\n"

        content += (
            f"% {result.uv_file_entry.header.place} {result.uv_file_entry.header.position.latitude}N "
            f"{result.uv_file_entry.header.position.longitude}W\n"
        )

        straylight_correction = correct_straylight(result.calculation_input.brewer_type)
        if straylight_correction == StraylightCorrection.UNDEFINED:
            straylight_correction = result.calculation_input.settings.default_straylight_correction
        second_line_parts = {
            "type": result.uv_file_entry.header.type,
            "coscor": f"{cos_cor_to_apply.value}{cloud_cover_value}",
            "tempcor": f"{round(result.temperature_correction, 3)}",
            "straylightcor": straylight_correction.value,
            "o3": f"{round(float(ozone), 3)}DU",
            "albedo": str(albedo),
            "alpha": str(aerosol.alpha),
            "beta": str(aerosol.beta),
            "uvr_source": result.calculation_input.calibration.source,
        }
        # We join the second line parts like <key>=<value> and separate them with a tabulation (\t)
        content += "% " + ("\t".join("=".join(_) for _ in second_line_parts.items())) + "\n"

        content += f"% wavelength(nm)	spectral_irradiance(W m-2 nm-1)	time_hour_UTC\n"

        for i in range(len(result.spectrum.wavelengths)):
            content += (
                f"{result.spectrum.wavelengths[i]:.1f}\t "
                f"{result.spectrum.cos_corrected_spectrum[i] / 1000:.9f}\t   "  # converted to W m-2 nm-1
                f"{result.spectrum.measurement_times[i] / 60:.5f}\n"  # converted to hours
            )

        return content
コード例 #5
0
ファイル: result.py プロジェクト: pec0ra/buvic
    def get_qasume_name(self, prefix: str = "", suffix: str = "") -> str:
        """
        Create a name specific to this result.

        :param prefix: the prefix to add to the file name
        :param suffix: the suffix to add to the file name
        :return: the created file name
        """
        bid = self.calculation_input.brewer_id
        days = date_to_days(self.calculation_input.date)
        time = minutes_to_time(self.spectrum.measurement_times[0])

        file_name = f"{prefix}{days:03}{time.hour:02}{time.minute:02}G.{bid}{suffix}"
        return path.join(self.get_relative_path(), file_name)
コード例 #6
0
def get_cloud_cover(latitude: float, longitude: float, d: date) -> CloudCover:
    if DARKSKY_TOKEN is None:
        LOG.warning(
            "DARKSKY_TOKEN environment variable is not defined. Functionality will be deactivated and 'clear_sky' will be used as "
            "default")
        warn(
            f"DARKSKY_TOKEN environment variable is not defined. Functionality is deactivated and 'clear_sky' "
            f"is used as default for cos correction.")
        return DefaultCloudCover()

    t = datetime.combine(d, time(0, 0, 0, 0)).isoformat()
    url_string = f"https://api.darksky.net/forecast/{DARKSKY_TOKEN}/{latitude},{-longitude},{t}?exclude=minutely,currently,daily&units=si"
    LOG.debug("Retrieving weather data from %s", url_string)
    try:
        req = urllib.request.Request(url_string)
        with urllib.request.urlopen(req) as url:
            data = json.loads(url.read().decode())
    except HTTPError as e:
        raise Exception(
            "Error while trying to access darksky. Please check your configuration and your quota."
        ) from e

    # Display a warning if madis isn't in the data sources
    if "madis" not in data["flags"]["sources"]:
        LOG.warning(
            "The data used as weather to choose between clear sky or diffuse correction might be imprecise. The data used came from"
            "unknown sources: %s",
            " ".join(data["flags"]["sources"]),
        )
        warn(
            f"The data used as weather to choose between clear sky or diffuse correction might be imprecise. The data"
            f" used came from unknown sources: {' '.join(data['flags']['sources'])}"
        )

    # Display a warning if the nearest weather station used is too far away
    nearest_station_distance = data["flags"]["nearest-station"]
    if nearest_station_distance > 30:
        LOG.warning(
            "The data used as weather to choose between clear sky or diffuse correction might be imprecise.\nThe nearest weather "
            "station found was %f km away from UV measurement.",
            nearest_station_distance,
        )
        warn(
            f"The data used as weather to choose between clear sky or diffuse correction might be imprecise.\n"
            f"The nearest weather station found was {nearest_station_distance} km away from UV measurement."
        )

    i = 0
    times = []
    values = []
    for hour_data in data["hourly"]["data"]:
        if "cloudCover" not in hour_data:
            LOG.warning(
                "No cloud cover data found for hour %d at date %s (%d). Value will be interpolated.",
                i, d.isoformat(), date_to_days(d))
            i += 1
            continue
        times.append(float(i * 60))
        values.append(hour_data["cloudCover"])
        i += 1

    if 24 > len(values) > 0:
        warn(
            f"Cloud cover data not found for some of the hours of this day. Interpolated values might be used."
        )

    if len(values) == 0:
        LOG.warning(
            "No cloud cover data found for date %s (%d). Default value will be used",
            d.isoformat(), date_to_days(d))
        warn(
            f"No cloud cover data found for date {d.isoformat()} ({date_to_days(d)}). Default value is used."
        )
        return DefaultCloudCover()

    return DarkskyCloudCover(times, values)