From ea09c9436861175449921841d4e0861d0fbda362 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Tue, 17 Sep 2024 17:18:26 -0400 Subject: [PATCH] add optional argument 'mode_key' to calc_footprint_for_trip This will allow the footprint to be calculated for a trip using either the mode_confirm or the replaced_mode_confirm. Expanded the tests to demonstrate how this would be used to determine the "impact" of the ebike in a trip where ebike replaces car. - changed expacted values to 3 decimal places because ebikes are so efficient the values would round to 0.00 --- .../footprint/footprint_calculations.py | 4 +- src/emcommon/metrics/metrics_summaries.py | 1 + test/metrics/test_footprint_calculations.py | 72 +++++++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/emcommon/metrics/footprint/footprint_calculations.py b/src/emcommon/metrics/footprint/footprint_calculations.py index ab33a8c..a43bf68 100644 --- a/src/emcommon/metrics/footprint/footprint_calculations.py +++ b/src/emcommon/metrics/footprint/footprint_calculations.py @@ -11,13 +11,13 @@ import emcommon.metrics.footprint.util as emcmfu -async def calc_footprint_for_trip(trip, label_options, mode_value=None, labels_map=None): +async def calc_footprint_for_trip(trip, label_options, mode_key='mode', mode_value=None, labels_map=None): """ Calculate the estimated footprint of a trip, which includes 'kwh' and 'kg_co2' fields. """ trip = dict(trip) # Log.debug(f"Getting footprint for trip: {str(trip)}") - mode_value = mode_value or emcdu.label_for_trip(trip, 'mode', labels_map) + mode_value = mode_value or emcdu.label_for_trip(trip, mode_key, labels_map) rich_mode = mode_value and emcdb.get_rich_mode_for_value(mode_value, label_options) is_uncertain = False if rich_mode is None or 'footprint' not in rich_mode: diff --git a/src/emcommon/metrics/metrics_summaries.py b/src/emcommon/metrics/metrics_summaries.py index 96bbbcc..0fe968f 100644 --- a/src/emcommon/metrics/metrics_summaries.py +++ b/src/emcommon/metrics/metrics_summaries.py @@ -73,6 +73,7 @@ async def value_of_metric_for_trip(metric_name: str, grouping_field: str, groupi elif metric_name == 'footprint': (footprint, metadata) = await emcmff.calc_footprint_for_trip(trip, app_config['label_options'], + 'mode', grouping_val, labels_map) footprint.update({'metadata': metadata}) diff --git a/test/metrics/test_footprint_calculations.py b/test/metrics/test_footprint_calculations.py index 277aba0..1d62b6c 100644 --- a/test/metrics/test_footprint_calculations.py +++ b/test/metrics/test_footprint_calculations.py @@ -5,7 +5,7 @@ @jest_test -async def test_car_default_footprint(): +async def test_car_footprint(): """ Test kWh and kg CO2e for a 1 km trip in the default CAR. """ @@ -24,22 +24,47 @@ async def test_car_default_footprint(): (footprint, metadata) = await emcmff.calc_footprint_for_trip(fake_trip, fake_label_options) - expected_footprint = {'kwh': 0.90, 'kg_co2': 0.29} + expected_footprint = {'kwh': 0.899, 'kg_co2': 0.291} for key in expected_footprint.keys(): - expectAlmostEqual(footprint[key], expected_footprint[key], delta=0.01) + expectAlmostEqual(footprint[key], expected_footprint[key], delta=0.001) # with 2 passengers, the footprint should be halved fake_label_options['MODE'][0]['passengers'] = 2 (footprint, metadata) = await emcmff.calc_footprint_for_trip(fake_trip, fake_label_options) - expected_footprint = {'kwh': 0.90 / 2, 'kg_co2': 0.29 / 2} + expected_footprint = {'kwh': 0.899 / 2, 'kg_co2': 0.291 / 2} for key in expected_footprint.keys(): - expectAlmostEqual(footprint[key], expected_footprint[key], delta=0.01) + expectAlmostEqual(footprint[key], expected_footprint[key], delta=0.001) @jest_test -async def test_car_custom_footprint(): +async def test_ebike_footprint(): + """ + Test kWh and kg CO2e for a 1 km trip in the default E_BIKE. + """ + fake_trip = { + "_id": "fake_trip_id", + "distance": 1000, + "start_fmt_time": "0000-00-00T00:00:00", + "start_loc": {"coordinates": [0, 0]}, + "user_input": {"mode_confirm": "ebike"} + } + fake_label_options = { + "MODE": [ + {"value": "ebike", "base_mode": "E_BIKE"} + ] + } + + (footprint, metadata) = await emcmff.calc_footprint_for_trip(fake_trip, fake_label_options) + + expected_footprint = {'kwh': 0.014, 'kg_co2': 0.006} + for key in expected_footprint.keys(): + expectAlmostEqual(footprint[key], expected_footprint[key], delta=0.001) + + +@jest_test +async def test_custom_car_footprint(): """ Test kWh and kg CO2e for a 10 km trip in a custom CAR (wh/km = 100). """ @@ -100,4 +125,39 @@ async def test_nyc_bus_footprint(): expectEqual(metadata[key], expected_metadata[key]) +@jest_test +async def test_impact_of_ebike_replacing_car(): + """ + Test kWh and kg CO2e saved for a 1 km trip where E_BIKE replaced CAR. + """ + fake_trip = { + "_id": "fake_trip_id", + "distance": 1000, + "start_fmt_time": "0000-00-00T00:00:00", + "start_loc": {"coordinates": [0, 0]}, + "user_input": {"mode_confirm": "ebike", "replaced_mode_confirm": "car"} + } + fake_label_options = { + "MODE": [ + {"value": "car", "base_mode": "CAR"}, + {"value": "ebike", "base_mode": "E_BIKE"} + ] + } + + (mode_footprint, metadata) = await emcmff.calc_footprint_for_trip( + fake_trip, + fake_label_options + ) + (replaced_mode_footprint, metadata) = await emcmff.calc_footprint_for_trip( + fake_trip, + fake_label_options, + 'replaced_mode' + ) + kwh_saved = replaced_mode_footprint['kwh'] - mode_footprint['kwh'] + kg_co2_saved = replaced_mode_footprint['kg_co2'] - mode_footprint['kg_co2'] + + expectAlmostEqual(kwh_saved, 0.885, delta=0.001) + expectAlmostEqual(kg_co2_saved, 0.285, delta=0.001) + + jest_describe("test_footprint_calculations")