Skip to main content

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

Project description

urban-dollop

A Python package for urban freight simulation based on the MASS-GT multi-agent model. Consolidates the original six-container LEAD pipeline into a single installable library — one pip install replaces six Docker images.

Built as academic research: every step's mathematical model is documented alongside the API so the simulation is fully transparent and citable.


Install

pip install urban-dollop

For performance-critical runs (enables Numba JIT on hot loops):

pip install urban-dollop[fast]

Usage

Each pipeline step is a plain function: it accepts data objects and returns a pandas.DataFrame. No files are written unless you ask — the library produces results; you decide what to do with them.

Step 1 — Demand generation

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

from urban_dollop import Zone, Depots, CarrierShares, DemandData, generate_demand

data = DemandData(
    zones          = Zone("zones.gpkg"),
    depots         = Depots("depots.gpkg"),
    skim_time      = "skim_time.mtx",
    carrier_shares = CarrierShares("shares.csv"),
    study_area     = ["Joinville"],
)

parcels = generate_demand(data)

parcels is a pandas.DataFrame — one row per parcel:

column type description
parcel_id int unique parcel identifier
origin_zone int zone ID of the originating depot
destination_zone int zone ID of the delivery address
depot_id int depot that handles this parcel
carrier str carrier name (as supplied in carrier_shares)
vehicle_type int vehicle type code (7 = van)
locker_zone int zone ID if routed to a parcel locker; 0 otherwise
segment str "B2C" (business-to-consumer) or "C2C" (consumer-to-consumer)
local_to_local bool parcel originates and delivers within the study area
crowdship_eligible bool eligible for crowdshipping (always a subset of local_to_local)
fulfilment_type str "Hubspoke" or "Hyperconnected"

Save as CSV:

parcels.to_csv("parcel_demand.csv", index=False)

Save as a GIS layer (QGIS, ArcGIS):

from urban_dollop import demand_layer

# attach zone geometry and aggregate by destination zone
gdf = demand_layer(parcels, data.zones)
gdf.to_file("parcel_demand.gpkg")   # drag directly into QGIS

gdf is a geopandas.GeoDataFrame — one row per zone — with the original zone geometry plus:

column description
parcel_count total parcels delivered to this zone
hubspoke_count via standard depot-to-door fulfilment
hyperconnected_count via local / crowdshipping fulfilment
b2c_count business-to-consumer parcels
c2c_count consumer-to-consumer parcels
local_to_local_count parcels that originate within the study area

Column mapping — when your file uses different names:

Each input type expects canonical column names by default. If your files use different names, supply a mapping — only the columns that differ:

data = DemandData(
    zones = Zone("zones.gpkg", columns={
        "zone_id":    "id",
        "households": "hh_count",
        "jobs":       "employment",
    }),
    depots = Depots("depots.gpkg", columns={
        "carrier": "operator",
    }),
    skim_time      = "skim_time.mtx",
    carrier_shares = CarrierShares("shares.csv", columns={
        "share": "market_share",
    }),
    study_area = ["Joinville"],
)

Canonical column names:

input column description
Zone zone_id unique integer zone identifier
Zone municipality municipality name (used for study_area)
Zone households household count
Zone jobs job / employee count
Depots depot_id unique integer depot identifier
Depots zone_id zone the depot is located in
Depots carrier carrier / operator name
CarrierShares share market share fraction (sums to 1)

Adjust calibration parameters:

from urban_dollop import DemandConfig

parcels = generate_demand(
    data,
    DemandConfig(
        parcels_per_household   = 0.20,
        delivery_success_b2c    = 0.80,
        local_to_local_fraction = 0.08,
        random_seed             = 42,
    ),
)

Mathematical model

Step 1 — Parcel demand generation

Zonal demand

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

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

symbol DemandConfig field description
$H_z$ households in zone $z$ (woningen)
$E_z$ employees in zone $z$ (arbeidspl)
$r_{HH}$ parcels_per_household parcels per household per day
$r_E$ parcels_per_employee parcels per employee per day
$s_{B2C}^0$ delivery_success_b2c baseline first-attempt success rate, B2C
$s_{B2B}^0$ delivery_success_b2b baseline first-attempt success rate, B2B

When a parcel locker is present in zone $z$, the effective success rate is raised:

$$s_{B2C}(z) = \alpha_z + (1 - \alpha_z), s_{B2C}^0$$

where $\alpha_z = \ell \cdot \mathbf{1}[z \in \mathcal{L}]$, $\mathcal{L}$ is the set of zones with lockers (locker_zones), and $\ell$ is the locker adoption rate (locker_adoption_rate).

Carrier split

Total 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$ (column share in carrier_shares).

Depot assignment

Each parcel is assigned to the nearest depot of its carrier by travel time:

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

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

Market segment

Each parcel is independently assigned a segment:

$$\text{segment}i \sim \text{Bernoulli}(p{C2C}), \qquad p_{C2C} = \frac{r_{HH,C2C}}{r_{HH}}$$

where $r_{HH,C2C}$ is parcels_per_household_c2c.

Local-to-local, crowdshipping, and fulfilment type

A fraction $f_{L2L}$ of parcels are flagged local-to-local; of those, a fraction $f_{CS}$ are crowdshipping-eligible:

$$\text{L2L}i \sim \text{Bernoulli}(f{L2L})$$

$$\text{CS}_i \sim \text{L2L}i \cdot \text{Bernoulli}(f{CS})$$

For L2L parcels, the origin zone is resampled from the study-area household distribution (reflecting that senders are residents, not distant warehouses):

$$P(\text{origin}i = z) = \frac{H_z}{\displaystyle\sum{z'} H_{z'}}$$

Fulfilment type is then assigned deterministically:

$$\text{fulfilment}_i = \begin{cases} \text{Hyperconnected} & \text{if } \text{L2L}_i \lor \text{CS}_i \lor (\text{locker}_i \neq 0) \ \text{Hubspoke} & \text{otherwise} \end{cases}$$


History and research context

Forked from MASS-GT, a multi-agent simulation system for urban goods transport originally developed for the Dutch Randstad region.

This refactor is part of ongoing academic research with two primary contributions:

  1. Unified package. The original six-container LEAD microservice architecture is dissolved into a single in-memory Python pipeline. Shared data (zone maps, skim matrices) is loaded once and passed through all steps without intermediate files.

  2. Grade-aware emission accounting. The original COPERT emission model assumes flat roads. This package introduces road gradient as a third coordinate into the emission step via the Vehicle Specific Power (VSP) formulation, enabling topographically accurate emission estimates in hilly cities. The Joinville (Brazil) case study, with elevation ranging from 0 to 881 m, is the primary validation dataset.

The original LEAD Packaging and Modeler Version source files are preserved in tmp/ during the refactoring process.

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.1.0.tar.gz (265.9 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.1.0-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: urban_dollop-0.1.0.tar.gz
  • Upload date:
  • Size: 265.9 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.1.0.tar.gz
Algorithm Hash digest
SHA256 ebabf6115b503886959171fc1cc366a22c5f2a29d3bda925f220b67f6b8928d8
MD5 6348ffb5c91dbccb29f69f74ca412500
BLAKE2b-256 ee38ce0dad9dfb08daef342993e48aad8f5c2be597e794b224516b4c793f17d2

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: urban_dollop-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.4 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b24a1013f9ab6465a59b58de25323c3f5c30bdaf5c5668492e4722c7c9773e2a
MD5 91624b61774d9891b47992cb74e4bf1a
BLAKE2b-256 76f1b1334788f3df41e76dda5da6d94f82662b79b0acd1898637aad99316b5ed

See more details on using hashes here.

Provenance

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