示例#1
0
def _filter_met(met, start, num_hours):
    # the passed-in met is a reference to the fires_manager's met, so copy it
    met = copy.deepcopy(met)

    if not met:
        # return `met` in case it's a dict and dict is expected downstream
        return met

    # limit met to only what's needed to cover dispersion window
    end = start + datetime.timedelta(hours=num_hours)

    # Note: we don't store the parsed first and last hour values
    # because they aren't used outside of this method, and they'd
    # just have to be dumped back to string values when bsp exits
    logging.debug('Determinig met files needed for dispersion')
    met_files = met.pop('files', [])
    met["files"] = []
    for m in met_files:
        if (parse_datetime(m['first_hour']) <= end
                and parse_datetime(m['last_hour']) >= start):
            met["files"].append(m)
        else:
            logging.debug('Dropping met file %s - not needed for dispersion',
                          m["file"])
    return met
示例#2
0
    def _within_time_range(self, active_area):
        """
        Note that all times in the activity objects (activity 'start'
        and 'end' times, as well as timeprofile and hourly_frp keys)
        are already in local time.

        Also note that, though unlikely, the locations (specified points
        and/or perimeter) in the active area could have different utc offsets.
        """
        if active_area.get('start') and active_area.get('end'):
            utc_offsets = set(
                [self._get_utc_offset(loc) for loc in active_area.locations])

            # convert to datetime objects in place
            active_area['start'] = parse_datetime(active_area.get('start'),
                                                  'start')
            active_area['end'] = parse_datetime(active_area.get('end'), 'end')

            is_within = False
            for utc_offset in utc_offsets:
                # the activity object's 'start' and 'end' will be in local time;
                # convert them to UTC to compare with start/end query parameters
                utc_start = active_area['start'] - utc_offset
                utc_end = active_area['end'] - utc_offset

                is_within = is_within or (
                    (not self._start or utc_end >= self._start) and
                    (not self._end or utc_start <= self._end))

            return is_within

        return False  # not necessary, but makes code more readable
示例#3
0
def _get_profiler(hourly_fractions, fire, active_area):
    tw = parse_datetimes(active_area, 'start', 'end')

    # Use FepsTimeProfiler for Rx fires and StaticTimeProfiler for WF,
    # Unless custom hourly_fractions are specified, in which case
    # Static Time Profiler is used for all fires.
    # If ignition_start and ignition_end aren't specified for Rx fires,
    # FepsTimeProfiler will assume 9am-12pm
    # TODO: add config setting to use FEPS for Rx even if custom
    #   hourly_fractions are specified (or the converse - i.e. alwys use
    #   FEPS for rx and add setting to turn on use of hourly_fractions,
    #   if specified, for Rx)
    if fire.type == 'rx' and not hourly_fractions:
        ig_start = active_area.get('ignition_start') and parse_datetime(
            active_area['ignition_start'], k='ignition_start')
        ig_end = active_area.get('ignition_end') and parse_datetime(
            active_area['ignition_end'], k='ignition_end')
        # TODO: pass in duff_fuel_load, total_above_ground_consumption,
        #    total_below_ground_consumption, moisture_category,
        #    relative_humidity, wind_speed, and duff_moisture_content,
        #    if defined?
        return FepsTimeProfiler(tw['start'],
                                tw['end'],
                                local_ignition_start_time=ig_start,
                                local_ignition_end_time=ig_end,
                                fire_type=FireType.RX)

    else:
        return StaticTimeProfiler(tw['start'],
                                  tw['end'],
                                  hourly_fractions=hourly_fractions)
示例#4
0
    def __init__(self, **config):
        self._config = config

        # start and end times, to use in filtering activity windows
        self._start = self._config.get('start')
        self._end = self._config.get('end')
        self._start = self._start and parse_datetime(self._start, 'start')
        self._end = self._end and parse_datetime(self._end, 'end')
        if self._start and self._end and self._start > self._end:
            raise BlueSkyConfigurationError(self.START_AFTER_END_ERROR_MSG)
示例#5
0
 def setup(self):
     # with open(FSV2_INPUT_FILENAME) as f:
     #     self._input = json.loads(f.read())
     with open(self.LOADED_FILENAME) as f:
         self._expected_output = json.loads(f.read())
     # convert timestamps to datetime objects
     for f in self._expected_output:
         for a in f.get('activity', []):
             for aa in a.get('active_areas', []):
                 aa['start'] = parse_datetime(aa['start'])
                 aa['end'] = parse_datetime(aa['end'])
示例#6
0
    def _load_expected_output(self, filename):
        # with open(FSV2_INPUT_FILENAME) as f:
        #     self._input = json.loads(f.read())
        with open(filename) as f:
            expected = json.loads(f.read())
            expected.sort(key=lambda f: f['id'])
        # convert timestamps to datetime objects
        for f in expected:
            for a in f.get('activity', []):
                for aa in a.get('active_areas', []):
                    aa['start'] = parse_datetime(aa['start'])
                    aa['end'] = parse_datetime(aa['end'])
                    # Note: timeprofile keys are expected to be strings

        return expected
示例#7
0
    def _within_time_range(self, a):
        utc_offset = self._get_utc_offset(a)
        if a.get('start') and a.get('end'):
            # convert to datetime objects in place
            a['start'] = parse_datetime(a.get('start'), 'start')
            a['end'] = parse_datetime(a.get('end'), 'end')
            # the activity object's 'start' and 'end' will be in local time;
            # convert them to UTC to compare with start/end query parameters
            utc_start = a['start'] - utc_offset
            utc_end = a['end'] - utc_offset

            return ((not self._start or utc_end >= self._start)
                    and (not self._end or utc_start <= self._end))

        return False  # not necessary, but makes code more readable
示例#8
0
def _get_profiler(hourly_fractions, fire, active_area):
    tw = parse_datetimes(active_area, 'start', 'end')

    # Use FepsTimeProfiler for Rx fires and StaticTimeProfiler for WF,
    # Unless custom hourly_fractions are specified, in which case
    # Static Time Profiler is used for all fires.
    # If ignition_start and ignition_end aren't specified for Rx fires,
    # FepsTimeProfiler will assume 9am-12pm
    # TODO: add config setting to use FEPS for Rx even if custom
    #   hourly_fractions are specified (or the converse - i.e. alwys use
    #   FEPS for rx and add setting to turn on use of hourly_fractions,
    #   if specified, for Rx)
    if fire.type == 'rx' and not hourly_fractions:
        ig_start = active_area.get('ignition_start') and parse_datetime(
            active_area['ignition_start'], k='ignition_start')
        ig_end = active_area.get('ignition_end') and parse_datetime(
            active_area['ignition_end'], k='ignition_end')
        # TODO: pass in duff_fuel_load, total_above_ground_consumption,
        #    total_below_ground_consumption, moisture_category,
        #    relative_humidity, wind_speed, and duff_moisture_content,
        #    if defined?
        return FepsTimeProfiler(tw['start'],
                                tw['end'],
                                local_ignition_start_time=ig_start,
                                local_ignition_end_time=ig_end,
                                fire_type=FireType.RX)

    else:
        model_name = Config().get("timeprofile", "model").lower()
        if model_name == "ubc-bsf-feps":
            wfrtConfig = Config().get('timeprofile', 'ubc-bsf-feps')
            working_dir = wfrtConfig.get('working_dir')
            delete_if_no_error = wfrtConfig.get(
                'delete_working_dir_if_no_error')
            with osutils.create_working_dir(
                    working_dir=working_dir,
                    delete_if_no_error=delete_if_no_error) as wdir:
                fire_working_dir = os.path.join(
                    wdir, "feps-timeprofile-{}".format(fire.id))
                if not os.path.exists(fire_working_dir):
                    os.makedirs(fire_working_dir)
                return ubcbsffeps.UbcBsfFEPSTimeProfiler(
                    active_area, fire_working_dir, wfrtConfig)
        else:
            return StaticTimeProfiler(tw['start'],
                                      tw['end'],
                                      hourly_fractions=hourly_fractions)
示例#9
0
def _get_time_window():
    start = Config().get('trajectories', 'start')
    if not start:
        raise BlueSkyConfigurationError("Missing trajectories 'start' time")

    num_hours = Config().get('trajectories', 'num_hours')

    return parse_datetime(start), num_hours
示例#10
0
    def __init__(self, init_time, **config):  #, log_level):
        self.enabled = config and config.get('enabled')
        if self.enabled:
            # parse to datetime if necessary and then format appropriately
            self.init_time = parse_datetime(init_time).strftime('%Y%m%d%H')

            # TODO: error handling to catch undefined fields
            self.sl = statuslogging.StatusLogger(config.get('api_endpoint'),
                                                 config.get('api_key'),
                                                 config.get('api_secret'),
                                                 config.get('process'))
            self.domain = config.get('domain')
示例#11
0
        def _f(fire, working_dir):
            # TODO: create and change to working directory here (per fire),
            #   above (one working dir per all fires), or below (per activity
            #   window)...or just let plumerise create temp workingdir (as
            #   it's currently doing?
            for aa in fire.active_areas:
                start = aa.get('start')
                if not start:
                    raise ValueError(MISSING_START_TIME_ERROR_MSG)
                start = datetimeutils.parse_datetime(aa.get('start'), 'start')

                if not aa.get('timeprofile'):
                    raise ValueError(MISSING_TIMEPROFILE_ERROR_MSG)

                for loc in aa.locations:
                    if not loc.get('consumption', {}).get('summary'):
                        raise ValueError(MISSING_CONSUMPTION_ERROR_MSG)

                    # Fill in missing sunrise / sunset
                    if any([
                            loc.get(k) is None
                            for k in ('sunrise_hour', 'sunset_hour')
                    ]):

                        # default: UTC
                        utc_offset = datetimeutils.parse_utc_offset(
                            loc.get('utc_offset', 0.0))

                        # Use NOAA-standard sunrise/sunset calculations
                        latlng = locationutils.LatLng(loc)
                        s = sun.Sun(lat=latlng.latitude, lng=latlng.longitude)
                        d = start.date()
                        # just set them both, even if one is already set
                        loc["sunrise_hour"] = s.sunrise_hr(d, utc_offset)
                        loc["sunset_hour"] = s.sunset_hr(d, utc_offset)

                    fire_working_dir = _get_fire_working_dir(fire, working_dir)
                    plumerise_data = pr.compute(aa['timeprofile'],
                                                loc['consumption']['summary'],
                                                loc,
                                                working_dir=fire_working_dir)
                    loc['plumerise'] = plumerise_data['hours']

                    if config.get("load_heat"):
                        if 'fuelbeds' not in loc:
                            raise ValueError(
                                "Fuelbeds should exist before loading heat in plumerise"
                            )
                        loc["fuelbeds"][0]["heat"] = _loadHeat(
                            fire_working_dir)
示例#12
0
    def start(self):
        """Returns start of initial activity window

        Doesn't memoize, in case activity windows are added/removed/modified
        """
        # consider only activity windows with start times
        activity = [a for a in self.get('activity', []) if a.get('start')]
        if activity:
            activity = sorted(activity, key=lambda a: a['start'])
            # record  utc offset of initial activity window, in case
            # start_utc is being called
            self.__utc_offset = activity[0].get('location',
                                                {}).get('utc_offset')
            return datetimeutils.parse_datetime(activity[0]['start'], 'start')
示例#13
0
    def end(self):
        """Returns end of final activity window

        Doesn't memoize, in case activity windows are added/removed/modified

        TODO: take into account possibility of activity objects having different
          utc offsets.  (It's an extreme edge case where one start/end string
          is gt/lt another when utc offset is ignored but not when utc offset
          is considered, so this isn't a high priority)
        """
        # consider only activie areas with end times
        active_areas = [a for a in self.active_areas if a.get('end')]
        if active_areas:
            active_areas = sorted(active_areas, key=lambda a: a['end'])
            # record utc offset of initial active area, in case
            # start_utc is being called
            self.__utc_offset = active_areas[-1].get('utc_offset')
            return datetimeutils.parse_datetime(active_areas[-1]['end'], 'end')
示例#14
0
        def _f(fire):
            # TODO: create and change to working directory here (per fire),
            #   above (one working dir per all fires), or below (per activity
            #   window)...or just let plumerise create temp workingdir (as
            #   it's currently doing?
            for a in fire.activity:
                if not a.get('consumption', {}).get('summary'):
                    raise ValueError("Missing fire activity consumption data "
                        "required for FEPS plumerise")

                # Fill in missing sunrise / sunset
                if any([a['location'].get(k) is None for k in
                        ('sunrise_hour', 'sunset_hour')]):
                    start = datetimeutils.parse_datetime(a['start'], 'start')
                    if not start:
                        raise ValueError("Missing fire activity start time "
                            "required by FEPS plumerise")

                    # default: UTC
                    utc_offset = datetimeutils.parse_utc_offset(
                        a['location'].get('utc_offset', 0.0))

                    # Use NOAA-standard sunrise/sunset calculations
                    latlng = locationutils.LatLng(a['location'])
                    s = sun.Sun(lat=latlng.latitude, lng=latlng.longitude)
                    d = start.date()
                    # just set them both, even if one is already set
                    a['location']["sunrise_hour"] = s.sunrise_hr(d, utc_offset)
                    a['location']["sunset_hour"] = s.sunset_hr(d, utc_offset)

                if not a.get('timeprofile'):
                    raise ValueError("Missing timeprofile data required for "
                        "computing FEPS plumerise")

                plumerise_data = pr.compute(a['timeprofile'],
                    a['consumption']['summary'], a['location'],
                    working_dir=_get_working_dir(fire))
                a['plumerise'] = plumerise_data['hours']