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 output

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.


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.1.tar.gz (7.3 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.1-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: urban_dollop-0.3.1.tar.gz
  • Upload date:
  • Size: 7.3 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.1.tar.gz
Algorithm Hash digest
SHA256 516acc3f96a59f33999c78f2a736e024e9bcd22bd0ed988b6964e0801623416e
MD5 2dcad5d5434249ffdcca85714250197d
BLAKE2b-256 a7a539d570c546879df9e1451b43f80d78ea3468b34949942108e503356d2011

See more details on using hashes here.

Provenance

The following attestation bundles were made for urban_dollop-0.3.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: urban_dollop-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 22.8 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 289394ed72f36299e2994894b3e5e24ead62096b5e88157c0435c2d6504a6ecc
MD5 febb6d2513426cc801731f30dc6c317f
BLAKE2b-256 e51e00e8fce7681e22f3b0c50485eec62ad6e16eff084555558d8766bc887616

See more details on using hashes here.

Provenance

The following attestation bundles were made for urban_dollop-0.3.1-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