Skip to main content

GHG reduction calculator for transportation demand management measures (CAPCOA 2024 Handbook)

Project description

tdm-ghg

A Python library for calculating GHG emission reductions from Transportation Demand Management (TDM) measures, based on the CAPCOA 2024 Handbook for Analyzing GHG Emission Reductions, Assessing Climate Vulnerabilities, and Advancing Health and Equity (transportation section).

Given a project's characteristics (density, location type, land use), tdm-ghg returns the expected percent reduction in vehicle miles traveled (VMT) and associated GHG emissions for each applicable CAPCOA mitigation measure and combines them using the handbook's multiplicative dampening rules and subsector caps.

Installation

pip install tdm-ghg

Or install from source for development:

git clone https://github.com/your-org/tdm-ghg-calculator.git
cd tdm-ghg-calculator
pip install -e ".[dev]"

Quick Start

Context-aware usage

Build a TDMContext describing your project, then let the subsector orchestrators automatically select applicable measures and combine results:

from tdm_ghg import TDMContext, Scale, LocationType, LandUseType
from tdm_ghg import (
    run_land_use, run_trip_reduction, run_parking_management,
    run_neighborhood_design, run_transit, run_school_programs,
    run_clean_vehicles, run_multi_subsector,
)

ctx = TDMContext(
    scale=Scale.PROJECT_SITE,
    location_type=LocationType.URBAN,
    land_use_type=LandUseType.RESIDENTIAL,
    params={
        "proposed_residential_density": 20.0,
        "transit_mode_share": 0.05,
        "vehicle_mode_share": 0.80,
    },
)

# Run a single subsector
land_use_reduction = run_land_use(ctx)          # e.g. -0.24 (24% reduction)

# Run all combinable subsectors (Land Use + Neighborhood Design + Parking + Transit)
total = run_multi_subsector(ctx)                # capped at -0.70

Declaring Strategies and Parameter Resolution

Declare the strategies in use with TDMContext.measures (recommended). Only the declared measures run, shared params feed all of them, and selection problems fail loudly with MeasureSelectionError (unknown ID, measure inapplicable to the context, measure excluded by the orchestrator, or missing required parameters):

ctx = TDMContext(
    scale=Scale.PROJECT_SITE,
    location_type=LocationType.URBAN,
    land_use_type=LandUseType.RESIDENTIAL,
    measures=["T-1", "T-3", "T-6"],             # explicit strategy declaration
    params={
        "proposed_residential_density": 20.0,   # shared where applicable
        "transit_mode_share": 0.05,
        "vehicle_mode_share": 0.80,
        "pct_employees_eligible": 1.0,
    },
)

When measures is None (default), measures auto-activate whenever their required parameters are present in params — convenient, but a shared name (e.g. pct_employees_eligible) can activate several measures at once, so the explicit declaration is preferred.

TDMContext.params is matched to each measure's function arguments by name, with two layers:

  • Flat (shared) — a top-level entry applies to every selected measure whose signature accepts it. Ideal for regional context (mode shares, trip lengths, emission factors) that must stay consistent across measures.
  • Measure-scoped — an entry keyed by a measure ID holds a sub-dict that applies only to that measure, overriding flat values. Use it when the same name means different things to different measures (e.g. transit_mode_share in T-3 vs. T-9).
params = {
    "transit_mode_share": 0.02,                 # flat default for measures that accept it
    "T-3": {"transit_mode_share": 0.10,         # T-3 gets its own (city-wide) value
            "vehicle_mode_share": 0.80},
    "T-6": {"pct_employees_eligible": 1.0},     # scope a value (and, in auto mode,
}                                               # activation) to T-6 only

Direct function usage

Every measure function is importable for standalone use:

from tdm_ghg import t1_increase_residential_density

result = t1_increase_residential_density(proposed_residential_density=20.0)
# -0.2637... (26.4% reduction in project VMT)

Inspecting the registry

All registered measures and their metadata are accessible at runtime:

from tdm_ghg import registry

for measure_id, meta in registry.measures.items():
    print(f"{measure_id}: {meta.name} (max {meta.measure_max:.0%})")

Return Value Convention

All mitigation functions return a decimal fraction where negative values represent reductions:

Return value Meaning
-0.14 14% reduction in VMT/GHG
-0.30 30% reduction (measure cap hit)
0.0 No effect

Implemented Measures

Land Use (Project/Site) -- cap 65%

ID Function Applies to
T-1 t1_increase_residential_density Residential
T-2 t2_increase_job_density Commercial
T-3 t3_provide_transit_oriented_development All
T-4 t4_integrate_affordable_housing Residential
T-55 t55_infill_development Residential (excl. T-1, T-3)

Trip Reduction Programs (Project/Site) -- cap 45% commute VMT

ID Function Notes
T-5 t5_implement_voluntary_commute_trip_reduction Excl. T-6, T-7 through T-11
T-6 t6_implement_mandatory_commute_trip_reduction Excl. T-5, T-7 through T-11
T-7 t7_implement_commute_trip_reduction_marketing Excl. T-5, T-6
T-8 t8_provide_ridesharing_program Excl. T-5, T-6
T-9 t9_implement_subsidized_transit_program Excl. T-5, T-6
T-10 t10_provide_end_of_trip_bicycle_facilities Excl. T-5, T-6
T-11 t11_provide_employer_sponsored_vanpool Excl. T-5, T-6
T-12 t12_price_workplace_parking Excl. T-13
T-13 t13_implement_employee_parking_cash_out Excl. T-12

Parking Management (Project/Site) -- cap 35%

ID Function Notes
T-14 t14_provide_ev_charging_infrastructure
T-15 t15_limit_residential_parking_supply Residential only
T-16 t16_unbundle_residential_parking_costs Residential only

Land Use (Plan/Community) -- cap 30%

ID Function Notes
T-17 t17_improve_street_connectivity

Neighborhood Design (Plan/Community) -- cap 10%

ID Function Notes
T-18 t18_provide_pedestrian_network_improvement
T-19-A t19a_construct_or_improve_bike_facility Class I, II, or IV bike facility
T-19-B t19b_construct_or_improve_bike_boulevard Class III bike boulevard
T-20 t20_expand_bikeway_network
T-21-A t21a_implement_conventional_carshare
T-21-B t21b_implement_electric_carshare
T-22-A t22a_implement_pedal_bikeshare
T-22-B t22b_implement_electric_bikeshare
T-22-C t22c_implement_scootershare
T-22-D t22d_transition_conventional_to_electric_bikeshare

Trip Reduction Programs (Plan/Community) -- cap 2.3% commute VMT

ID Function Notes
T-23 t23_provide_community_based_travel_planning Residential only

Parking Management (Plan/Community) -- cap 30%

ID Function Notes
T-24 t24_implement_market_price_public_parking

Transit (Plan/Community) -- cap 15%

ID Function Notes
T-25 t25_extend_transit_network_coverage_or_hours
T-26 t26_increase_transit_service_frequency Excl. T-28 when BRT
T-27 t27_implement_transit_supportive_roadway_treatments Excl. T-28
T-28 t28_provide_bus_rapid_transit Excl. T-26, T-27, T-46
T-29 t29_reduce_transit_fares
T-46 t46_provide_transit_shelters Excl. T-28 when BRT

Clean Vehicles and Fuels (Plan/Community) -- cap 100%

ID Function Notes
T-30 t30_use_cleaner_fuel_vehicles BEV, PHEV, or well-to-wheels (WTW) modes

School Programs (Project/Site) -- cap 72% school VMT

ID Function Notes
T-40 t40_establish_school_bus_program
T-56 t56_active_modes_transportation_youth

Combining Measures

Within a subsector, individual reductions are combined using multiplicative dampening with a subsector-specific cap. Because this library uses signed reductions (negative = reduction), each value rᵢ scales remaining VMT by (1 + rᵢ):

combined = ∏(1 + rᵢ) - 1          # signed form of CAPCOA's 1 - ∏(1 - |rᵢ|)
combined = max(combined, -cap)    # one-sided floor on the reduction

This prevents double-counting overlapping reductions and ensures results stay within documented CAPCOA limits. The cap bounds reductions only; a net increase is returned unchanged.

from tdm_ghg import multiplicative_dampening

combined = multiplicative_dampening([-0.10, -0.08, -0.05], max_reduction_percentage=-0.15)
# -0.15 (capped at 15%)

Key Concepts

  • Scale: PROJECT_SITE or PLAN_COMMUNITY. Measures from different scales must never be combined.

  • LocationType: URBAN, SUBURBAN, or RURAL -- based on census-tract development level (Salon 2014 neighborhood typology).

  • LandUseType: RESIDENTIAL, COMMERCIAL, MIXED, or SCHOOL -- filters measures to applicable land uses.

  • Mutual exclusivity: Some measures cannot be combined within a subsector:

    • Trip Reduction: T-5 (voluntary) and T-6 (mandatory) each exclude T-7 through T-11; T-12 and T-13 are mutually exclusive.
    • Transit: T-28 (BRT) excludes T-26, T-27, and T-46. The run_transit(use_brt=True) orchestrator handles this automatically.

    Exclusivity is enforced: if mutually exclusive measures are activated together, the orchestrator raises MeasureExclusivityError rather than silently combining them. Resolve by passing excluded_measure_ids to select one per conflict, or by scoping inputs per measure (below).

Testing

pytest
pytest --cov=tdm_ghg

License

AGPL (GNU Affero General Public License) -- see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tdm_ghg-0.2.0.tar.gz (86.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tdm_ghg-0.2.0-py3-none-any.whl (65.1 kB view details)

Uploaded Python 3

File details

Details for the file tdm_ghg-0.2.0.tar.gz.

File metadata

  • Download URL: tdm_ghg-0.2.0.tar.gz
  • Upload date:
  • Size: 86.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for tdm_ghg-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e5779800de116e017039bc936080db21dddf49e40f4563b07565314f433f2e9b
MD5 0cd17333f3f7a125627b4995be1a6945
BLAKE2b-256 0910524c84e9c4472ed7623df1e943608c1673707ee904a3f5b724c4c452315d

See more details on using hashes here.

File details

Details for the file tdm_ghg-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: tdm_ghg-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 65.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for tdm_ghg-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c38abda5c2c9ecfd12e51035922852a9ed6409a249bbb79757642ec23b32084
MD5 1d87e63cebc7911208a7610673fcfa3c
BLAKE2b-256 c5f743c2d0448310eafb72432d42d59382fa8de5c7d1e44f4b11d34f27521c7b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page