Skip to main content

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.

Release files for tdm-ghg 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tdm-ghg 0.2.1
File Size Uploaded
tdm_ghg-0.2.1.tar.gz 87.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tdm-ghg 0.2.1
File Interpreter ABI Platform
tdm_ghg-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 152.8 kB

Release files / tdm_ghg-0.2.1.tar.gz

Download URL tdm_ghg-0.2.1.tar.gz
Size 87.6 kB
Tags Source
SHA-256 checksum
How to use checksums
186a052d51c5056843c5b60302256fa7e070a964c6fdaa414709a9609131b2a0
BLAKE2b-256 checksum
How to use checksums
63622931a525a7e6ca801d696c0a4aed56279f49dab00ba9e84f575728c966f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Release files / tdm_ghg-0.2.1-py3-none-any.whl

Download URL tdm_ghg-0.2.1-py3-none-any.whl
Size 65.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d51e8be6bdb8104173c6b1c8c060da84c19c361816d56c1df5a9517627649820
BLAKE2b-256 checksum
How to use checksums
d4e15685fffcd1a2ba9cdee94fcb31dc75bdc832365109166b7a8ea44c5f0ea8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page