Example #1
0
    def __init__(self, fire_failure_handler):
        super(Consume, self).__init__(fire_failure_handler)

        self.species = self.species and [e.upper() for e in self.species]

        all_fuel_loadings = (Config().get('emissions', 'fuel_loadings')
                             or Config().get('consumption', 'fuel_loadings'))
        self.fuel_loadings_manager = FuelLoadingsManager(
            all_fuel_loadings=all_fuel_loadings)
Example #2
0
def run(fires_manager):
    """Runs the fire data through consumption calculations, using the consume
    package for the underlying computations.

    Args:
     - fires_manager -- bluesky.models.fires.FiresManager object
    """
    # TODO: don't hard code consume_version; either update consume to define
    # it's version in consume.__version__, or execute pip:
    #   $ pip3 freeze |grep consume
    #  or
    #   $ pip3 show apps-consume4|grep "^Version:"
    fires_manager.processed(__name__,
                            __version__,
                            consume_version=CONSUME_VERSION_STR)

    # TODO: get msg_level and burn_type from fires_manager's config
    msg_level = 2  # 1 => fewest messages; 3 => most messages

    all_fuel_loadings = Config().get('consumption', 'fuel_loadings')
    fuel_loadings_manager = FuelLoadingsManager(
        all_fuel_loadings=all_fuel_loadings)

    _validate_input(fires_manager)

    # TODO: can I safely instantiate one FuelConsumption object and
    # use it across all fires, or at lesat accross all fuelbeds within
    # a single fire?
    for fire in fires_manager.fires:
        with fires_manager.fire_failure_handler(fire):
            _run_fire(fire, fuel_loadings_manager, msg_level)

    datautils.summarize_all_levels(fires_manager, 'consumption')
    datautils.summarize_all_levels(fires_manager, 'heat')
Example #3
0
class Consume(EmissionsBase):
    def __init__(self, fire_failure_handler):
        super(Consume, self).__init__(fire_failure_handler)

        self.species = self.species and [e.upper() for e in self.species]

        all_fuel_loadings = (Config().get('emissions', 'fuel_loadings')
                             or Config().get('consumption', 'fuel_loadings'))
        self.fuel_loadings_manager = FuelLoadingsManager(
            all_fuel_loadings=all_fuel_loadings)

    def run(self, fires):
        logging.info("Running emissions module with CONSUME")

        # look for custom fuel loadings first in the emissions config and then
        # in the consumption config

        for fire in fires:
            with self.fire_failure_handler(fire):
                self._run_on_fire(fire)

    def _run_on_fire(self, fire):
        logging.debug("Consume emissions - fire {}".format(fire.get("id")))

        if 'activity' not in fire:
            raise ValueError(
                "Missing activity data required for computing consume emissions"
            )

        burn_type = fire.get("fuel_type") or 'natural'
        # TODO: set burn type to 'activity' if fire["fuel_type"] == 'piles' ?
        if burn_type == 'piles':
            raise ValueError("Consume can't be used for fuel type 'piles'")

        for aa in fire.active_areas:
            season = datetimeutils.season_from_date(aa.get('start'))
            for loc in aa.locations:
                if 'fuelbeds' not in loc:
                    raise ValueError(
                        "Missing fuelbed data required for computing emissions"
                    )

                for fb in loc['fuelbeds']:
                    self._run_on_fuelbed(aa, loc, fb, season, burn_type)

    def _run_on_fuelbed(self, active_area, loc, fb, season, burn_type):
        if 'consumption' not in fb:
            raise ValueError(
                "Missing consumption data required for computing emissions")
        if 'heat' not in fb:
            raise ValueError(
                "Missing heat data required for computing emissions")
        if 'pct' not in fb:
            raise ValueError(
                "Missing fuelbed 'ptc' required for computing emissions")
        if 'ecoregion' not in active_area:
            raise ValueError(
                "Missing ecoregion required for computing emissions")

        fuel_loadings_csv_filename = self.fuel_loadings_manager.generate_custom_csv(
            fb['fccs_id'])
        # unlike with consume consumption results, emissions results reflect
        # how you set area and output_units
        area = (fb['pct'] / 100.0) * loc['area']
        fc = FuelConsumptionForEmissions(fb["consumption"],
                                         fb['heat'],
                                         area,
                                         burn_type,
                                         fb['fccs_id'],
                                         season,
                                         active_area,
                                         fccs_file=fuel_loadings_csv_filename)

        e_fuel_loadings = self.fuel_loadings_manager.get_fuel_loadings(
            fb['fccs_id'], fc.FCCS)
        fb['emissions_fuel_loadings'] = e_fuel_loadings
        e = consume.Emissions(fuel_consumption_object=fc)
        e.output_units = 'tons'

        # Consume emissions prints out lines like
        #    Converting units: tons_ac -> tons
        # which we want to capture and ifnore
        with capture_stdout() as stdout_buffer:
            r = e.results()['emissions']

        fb['emissions'] = {f: {} for f in CONSUME_FIELDS}
        # r's key hierarchy is species > phase; we want phase > species
        for k in r:
            upper_k = 'PM2.5' if k == 'pm25' else k.upper()
            if k != 'stratum' and (not self.species
                                   or upper_k in self.species):
                for p in r[k]:
                    fb['emissions'][p][upper_k] = r[k][p]

        if self.include_emissions_details:
            # Note: consume gives details per fuel category, not per
            #  subcategory; to match what FEPS and Prichard/O'Neill calculators
            #  produce, put all per-category details under'summary'
            # The details are under key 'stratum'. the key hierarchy is:
            #    'stratum' > species > fuel category > phase
            #   we want phase > species:
            #     'summary' > fuel category > phase > species
            fb['emissions_details'] = {"summary": {}}
            for k in r.get('stratum', {}):
                upper_k = 'PM2.5' if k == 'pm25' else k.upper()
                if not self.species or upper_k in self.species:
                    for c in r['stratum'][k]:
                        fb['emissions_details']['summary'][c] = fb[
                            'emissions_details']['summary'].get(c, {})
                        for p in r['stratum'][k][c]:
                            fb['emissions_details']['summary'][c][p] = fb[
                                'emissions_details']['summary'][c].get(p, {})
                            fb['emissions_details']['summary'][c][p][
                                upper_k] = r['stratum'][k][c][p]
Example #4
0
    def test(self):
        # TODO: create mock fuel loading manager class
        fuel_loadings_manager = FuelLoadingsManager()
        consumption._run_fire(fire, fuel_loadings_manager, 1)
        fb = fire['activity'][0]['active_areas'][0]['specified_points'][0]['fuelbeds'][0]

        expected_heat = {
            'smoldering': [2412566576.4017043],
            'total': [6777717460.4055243],
            'residual': [1340653172.7602997],
            'flaming': [3024497711.2435193]
        }
        assert expected_heat == fb['heat']

        expected_consumption = {
            'canopy': {
                'ladder fuels': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'midstory': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'overstory': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'snags class 1 foliage': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'snags class 1 no foliage': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'snags class 1 wood': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'snags class 2': {'flaming': [0.0], 'residual': [0.0], 'smoldering': [0.0], 'total': [0.0]},
                'snags class 3': {'flaming': [0.0], 'residual': [0.0], 'smoldering': [0.0], 'total': [0.0]},
                'understory': {'flaming': [0.0],  'residual': [0.0],'smoldering': [0.0],'total': [0.0]}
            },
            'ground fuels': {
                'basal accumulations': {'flaming': [0.00045859784727272712],'residual': [0.0022929892363636353],'smoldering': [0.0018343913890909085], 'total': [0.0045859784727272706]},
                'duff lower': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                  'duff upper': {'flaming': [13.452203519999998],'residual': [26.904407039999995],'smoldering': [94.165424639999969],'total': [134.52203519999998]},
                  'squirrel middens': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]}
                },
            'litter-lichen-moss': {
            'lichen': {
                'flaming': [0.14868360324083274],'residual': [0.0],'smoldering': [0.007825452802149092],'total': [0.15650905604298185]},
                'litter': {'flaming': [50.821078916159998],'residual': [0.0],'smoldering': [5.6467865462399995],'total': [56.467865462399992]},
                'moss': {'flaming': [1.1665944254280725],'residual': [0.0],'smoldering': [0.061399706601477498],'total': [1.22799413202955]}
            },
            'nonwoody': {
                'primary live': {'flaming': [8.4133727999999994],'residual': [0.0],'smoldering': [0.93481920000000007],'total': [9.3481919999999992]},
                'secondary live': {'flaming': [0.42066864000000054],'residual': [0.0],'smoldering': [0.046740960000000061],'total': [0.46740960000000054]}
            },
            'shrub': {
                'primary live': {'flaming': [17.46396288],'residual': [0.0],'smoldering': [1.94044032],'total': [19.404403200000001]},
                'secondary live': {'flaming': [5.0504277600000016], 'residual': [0.0], 'smoldering': [0.56115864000000015], 'total': [5.6115864000000011]}
            },
            'summary': {
                'canopy': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                'ground fuels': {'flaming': [13.452662117847268], 'residual': [26.906700029236358], 'smoldering': [94.167259031389051], 'total': [134.52662117847271]},
                'litter-lichen-moss': {'flaming': [52.136356944828897],'residual': [0.0],'smoldering': [5.7160117056436262],'total': [57.852368650472521]},
                'nonwoody': {'flaming': [8.83404144], 'residual': [0.0],'smoldering': [0.98156016000000013],'total': [9.8156016000000008]},
                'shrub': {'flaming': [22.514390640000002],'residual': [0.0],'smoldering': [2.5015989599999999],'total': [25.015989600000001]},
                'total': {'flaming': [189.03110695271994],'residual': [83.790823297518727],'smoldering': [150.78541102510653],'total': [423.60734127534528]},
                'woody fuels': {'flaming': [92.093655810043785],'residual': [56.88412326828238],'smoldering': [47.418981168073863],'total': [196.39676024640005]}
            },
            'woody fuels': {
                '1-hr fuels': {'flaming': [16.219828799999998],'residual': [0.0],'smoldering': [0.85367520000000008],'total': [17.073504]},
                 '10-hr fuels': {'flaming': [34.573845600000006],'residual': [0.0],'smoldering': [3.8415384000000006],'total': [38.415384000000003]},
                 '100-hr fuels': {'flaming': [12.2128272],'residual': [0.71840160000000008],'smoldering': [1.4368032000000002],'total': [14.368031999999999]},
                 '1000-hr fuels rotten': {'flaming': [2.8541420126537145],'residual': [7.1353550316342869],'smoldering': [4.2812130189805719],'total': [14.270710063268574]},
                 '1000-hr fuels sound': {'flaming': [8.2870161230769241],'residual': [1.381169353846154],'smoldering': [4.1435080615384621],'total': [13.811693538461537]},
                 '10000-hr fuels rotten': {'flaming': [6.0119161543131447],'residual': [36.071496925878868],'smoldering': [18.035748462939434],'total': [60.11916154313144]},
                 '10000-hr fuels sound': {'flaming': [9.1157177353846173],'residual': [4.5578588676923086],'smoldering': [9.1157177353846173],'total': [22.789294338461541]},
                 '10k+-hr fuels rotten': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                 '10k+-hr fuels sound': {'flaming': [1.6574032246153847],'residual': [3.3148064492307694],'smoldering': [3.3148064492307694],'total': [8.2870161230769224]},
                 'piles': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                 'stumps lightered': {'flaming': [0.0],'residual': [0.0],'smoldering': [0.0],'total': [0.0]},
                 'stumps rotten': {'flaming': [0.61750584000000008],'residual': [3.7050350399999998],'smoldering': [1.8525175199999999],'total': [6.1750584000000002]},
                 'stumps sound': {'flaming': [0.54345312000000001],'residual': [0.0],'smoldering': [0.54345312000000001],'total': [1.08690624]}
            }
        }

        check_consumption(fb['consumption'], expected_consumption)