Skip to main content

Urban freight simulation based on the MASS-GT multi-agent model

Project description

urban-dollop

urban-dollop is a Python library that extracts and packages the mathematical models from MASS-GT — a multi-agent urban freight simulation system originally developed at TU Delft for the Dutch Randstad region. The models are reimplemented as a transparent, installable Python pipeline intended for academic research and reproducible urban logistics studies.

The primary novel contribution is the introduction of road grade as a dimension in emission accounting, enabling topographically accurate estimates in hilly cities.


Usage

Parcel demand generation

Estimate the daily parcel delivery demand for a study area from zonal population and employment data.

from urban_dollop import Zone, Depot, Carrier, SkimMatrix, generate_parcel_demand

zones    = Zone.from_file("zones.gpkg")
depots   = Depot.from_file("depots.gpkg")
carriers = Carrier.from_file("carrier_shares.csv")
skim     = SkimMatrix.from_file("skim_time.mtx", zones)

demands  = generate_parcel_demand(zones, depots, carriers, skim)

demands is a list[ParcelDemand] — one record per unique (destination zone, depot, vehicle type) combination:

field type description
destination_zone_id int zone ID of the delivery address
depot_id int depot that handles this flow
vehicle_type int vehicle type code (default 7 = van)
n_parcels int number of parcels in this flow

The origin zone of each flow is implicit — it is always the zone of the depot.

Save to CSV (join to your zones layer in QGIS or ArcGIS on destination_zone_id to map parcels delivered per zone):

ParcelDemand.to_file(demands, "parcel_demand.csv")

Calibration — via urban-dollop.toml:

Place an urban-dollop.toml in your current working directory to set calibration parameters without touching code:

[parcel_demand]
parcels_per_household = 0.2054  # B2C daily deliveries per household
parcels_per_employee  = 0.0     # B2B daily deliveries per employee
delivery_success_b2c  = 0.75    # first-attempt success rate, residential
delivery_success_b2b  = 0.95    # first-attempt success rate, commercial
default_vehicle_type  = 7       # 7 = van
random_seed           = 42

Calibration — programmatic override:

Pass a ParcelDemandConfig to override TOML values in code:

from urban_dollop import ParcelDemandConfig

demands = generate_parcel_demand(
    zones, depots, carriers, skim,
    config=ParcelDemandConfig(
        parcels_per_household = 0.178,
        parcels_per_employee  = 0.029,
        delivery_success_b2c  = 0.80,
        delivery_success_b2b  = 0.95,
        default_vehicle_type  = 7,
    ),
)

Programmatic values take precedence over TOML. Missing fields fall back to TOML. Pydantic raises if a required field is absent from both sources.

Column mapping — when your files use different column names:

zones = Zone.from_file("zones.gpkg", columns={
    "zone_id":    "id",
    "households": "hh_count",
    "employment": "jobs",
})

carriers = Carrier.from_file("shares.csv", columns={
    "name":  "courier",
    "share": "market_share",
})

Canonical column names:

model field description
Zone zone_id unique integer zone identifier
Zone households household count
Zone employment employee count
Depot depot_id unique integer depot identifier
Depot zone_id zone the depot is located in
Depot carrier carrier name (must match Carrier.name)
Carrier name carrier name
Carrier share market share fraction (all carriers must sum to 1)

CLI

Run the parcel demand module from canonical files:

urban-dollop generate-demand data/

This command:

  • reads urban-dollop.toml from the current working directory
  • reads zones.gpkg, depots.gpkg, carrier_shares.csv, and skim_time.mtx from data/
  • writes parcel_demand.csv to the current working directory by default

To write the CSV somewhere else, pass --outdir with either an existing directory or a full .csv path:

urban-dollop generate-demand --outdir results/ data/
urban-dollop generate-demand --outdir results/joinville_parcel_demand.csv data/

Example outputs

Parcel demand in Joinville by destination zone

[!NOTE] Example choropleth of simulated parcel deliveries aggregated by destination_zone_id for the Joinville fixture scenario. Zone geometry is based on IBGE territorial and census meshes, household counts on IBGE Census / SIDRA, employment on RAIS microdata, and travel times on OpenStreetMap routed with OSRM. Depot locations are scenario inputs compiled from public carrier and agency sources including Correios, Mercado Envios, Loggi, and Amazon. Carrier shares are scenario estimates synthesized from public market and company sources, including Correios and ABComm.

Parcel demand in Delft by destination zone

[!NOTE] Example choropleth of simulated parcel deliveries aggregated by destination_zone_id for the Delft fixture scenario. Study area covers five municipalities: Den Haag, Delft, Rijswijk, Leidschendam-Voorburg, and Midden-Delfland. Zone geometry, household counts, and employment are based on AHN and CBS data via the LEAD project (2020). Travel times are derived from the MASS-GT Netherlands skim matrix. Depot locations and carrier shares are sourced from the MASS-GT LEADVersion scenario.


Mathematical models

The following models are extracted from MASS-GT's parcel_dmnd module and implemented in generate_parcel_demand().

Parcel demand generation

Zonal demand

The number of parcel flows destined for zone $z$ on an average weekday:

$$D_z = \left\lfloor \frac{H_z \cdot r_{HH}}{s_{B2C}} + \frac{E_z \cdot r_E}{s_{B2B}} \right\rceil$$

symbol ParcelDemandConfig field description
$H_z$ households in zone $z$
$E_z$ employees in zone $z$
$r_{HH}$ parcels_per_household B2C parcels per household per day
$r_E$ parcels_per_employee B2B parcels per employee per day
$s_{B2C}$ delivery_success_b2c first-attempt delivery success rate, residential
$s_{B2B}$ delivery_success_b2b first-attempt delivery success rate, commercial

The success rates correct for failed first-attempt deliveries: the generated volume reflects shipments sent, not deliveries completed.

Carrier split

Total zonal demand is distributed across carriers by market share:

$$D_{z,k} = \left\lfloor \sigma_k \cdot D_z \right\rceil$$

where $\sigma_k$ is the market share of carrier $k$ (Carrier.share), and $\sum_k \sigma_k = 1$. This normalization is required by the implementation and validated before demand is allocated across carriers.

Depot assignment

Each (zone, carrier) flow is assigned to the nearest depot of that carrier by travel time from the depot zone to the destination zone:

$$\delta(z, k) = \underset{n \in \mathcal{N}k}{\arg\min}\ t(n{\text{zone}}, z)$$

where $\mathcal{N}k$ is the set of depots operated by carrier $k$ and $t(n{\text{zone}}, z)$ is the travel time in seconds from the depot's zone to zone $z$, read from the pre-computed skim matrix.

Aggregation

Flows that share the same destination zone, depot, and vehicle type are summed:

$$F_{z,n,v} = \sum_{k,:,\delta(z,k) = n} D_{z,k} \quad \text{for fixed vehicle type } v$$

Each resulting $(z, n, v)$ tuple with $F_{z,n,v} > 0$ becomes one ParcelDemand record.

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

urban_dollop-0.3.2.tar.gz (24.5 MB view details)

Uploaded Source

Built Distribution

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

urban_dollop-0.3.2-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file urban_dollop-0.3.2.tar.gz.

File metadata

  • Download URL: urban_dollop-0.3.2.tar.gz
  • Upload date:
  • Size: 24.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for urban_dollop-0.3.2.tar.gz
Algorithm Hash digest
SHA256 cbb2b4d5b69fdcd06feb456c48ac6410d932970d99387a54c73d4bd4a2f93d6a
MD5 6bd0ef3a0efe385ee4d53cad9f4f40fe
BLAKE2b-256 ac47e7723495d420e3dd28b401e4e10f11e6125ea0043224e1ea5a0e34092e91

See more details on using hashes here.

Provenance

The following attestation bundles were made for urban_dollop-0.3.2.tar.gz:

Publisher: deployment.yaml on hcubasd/urban-dollop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file urban_dollop-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: urban_dollop-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for urban_dollop-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cd141be8813aee3a30881e99c4a6b4d4981d2f6542b1e0a435cc3408632282ca
MD5 a069fa2e981725d402ee9bb2ec18005c
BLAKE2b-256 176ad23b3bca5cce6743cb8b2b2fffc2e1d8d2b7d11c1afffb52cfd85f873aec

See more details on using hashes here.

Provenance

The following attestation bundles were made for urban_dollop-0.3.2-py3-none-any.whl:

Publisher: deployment.yaml on hcubasd/urban-dollop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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