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.

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/
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 municipality municipality name
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)

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$.

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 and depot are summed:

$$F_{z,n} = \sum_{k,:,\delta(z,k) = n} D_{z,k}$$

Each resulting $(z, n)$ pair with $F_{z,n} > 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.0.tar.gz (262.0 kB 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.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: urban_dollop-0.3.0.tar.gz
  • Upload date:
  • Size: 262.0 kB
  • 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.0.tar.gz
Algorithm Hash digest
SHA256 ec6d66fbd70e0091584fd8dd2a6710a7bccc742184fcfca357be8c13ca8c05db
MD5 a09a61384dec1de4c682c2ad20f81cb3
BLAKE2b-256 e13be8cd3bf2148ac68b19a3eba7a6c1e39e4e54b6f161fe8da7991474b64b7b

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: urban_dollop-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 21.9 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d2564e62f14c68f998d3c0344dbc7c4f90437d066839a4e46a934b89f28d7b55
MD5 444caa90bad03d6d1eaa4838829ca141
BLAKE2b-256 878df9d6161adb85df065a5ab2dde26718c3f2ea88f62776bdd0700c6b256de5

See more details on using hashes here.

Provenance

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