Skip to content

Commit

Permalink
Merge pull request #3900 from LBNL-UCB-STI/tour-mode-clean-develop202403
Browse files Browse the repository at this point in the history
Tour mode -- again
  • Loading branch information
zneedell authored Oct 31, 2024
2 parents 518cadf + 2ab2024 commit 274e00d
Show file tree
Hide file tree
Showing 101 changed files with 5,443 additions and 1,090 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ plugins {
id "org.scoverage" version "8.0.3"
id 'maven-publish'
id "me.champeau.gradle.jmh" version "0.5.3"
id 'com.bmuschko.docker-java-application' version '6.7.0'
id 'com.bmuschko.docker-java-application' version '9.4.0'
id "cz.alenkacz.gradle.scalafmt" version "1.16.2"
id "java-library"
}
Expand All @@ -44,7 +44,7 @@ apply plugin: 'ManifestClasspath'
apply plugin: 'scalafmt'

group = 'beam'
version = '0.9.12'
version = '1.0.beta'

description = """"""

Expand All @@ -57,7 +57,7 @@ scala {

//java {
// toolchain {
// languageVersion.set(JavaLanguageVersion.of(11))
// languageVersion.set(JavaLanguageVersion.of(11))
// }
//}

Expand Down
59 changes: 54 additions & 5 deletions docs/behaviors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,69 @@ Person Agents in BEAM exhibit several within-day behaviors that govern their use
Mode Choice
-----------

The most prominent behavior is mode choice. Mode choice can be specified either exogensously as a field in the persons plans, or it can be selected during replanning, or it can remain unset and be selected within the day.
The most prominent behavior is mode choice. Mode choice can be specified either exogenously as a field in the persons plans, or it can be selected during replanning, or it can remain unset and be selected within the day. For agents that start the day at home, a tour can either be home based or nested within a home based tour (for instance, if an agent takes a lunch trip from work).

Within day mode choice is selected based on the attributes of the first trip of each tour. Once a mode is selected for the tour, the person attempts to stick with that mode for the duration of the tour.

In all cases (whether mode is specified before the day or chosen within the day) person agents use WALK as a fallback option throughout if constraints otherwise prevent their previously determined mode from being possible for any given trip. E.g. if a person is in the middle of a RIDE_HAIL tour, but the Ride Hail Manager is unable to match a driver to the person, then the person will walk.

In BEAM the following modes are considered:
Tour Mode vs Trip Mode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Individual plans can be considered as both sequences of trips and sequences of tours. Trips are defined by a travel leg and a destination activity, and each tour is defined as a sequence of trips that start and end at the same location.

Consider an agent who has a sequence of activities:

``Home`` --> ``Work`` --> ``Eat`` --> ``Work`` --> ``Shop`` --> ``Home``

Primary Home Based Tour
''''''''''''''''''''''''
**Home-based Tour** ---> ``Home`` --> ``Work`` --> ``Shop`` --> ``Home``

This tour includes the primary work trip, in addition to a shopping stop on the way home

Secondary Work Based Tour
''''''''''''''''''''''''
**Work-based Subtour** ---> ``Work`` --> ``Eat`` --> ``Work``

This subtour includes a work-based leg to go out to eat, followed by the return leg back to work


The motivation for dividing the plan into tours is largly due to to constraints around vehicle choice, where decisions made at the tour level impact all trips in the tour. For instance, if this agent chose to drive their car to work as their first trip of the day, they would be constrained to use that car for the rest of the trips in their primary home based tour such that the personal vehicle is returned home at the end of the day. However, the legs to and from the mid-day eating activity are considered as part of a secondary work-based tour rather than the primary home-based tour because the trip mode for these legs is *not* constrained to make use of the private car. Because the agent returns back to the same location at the end of the work based tour, they are able to leave behind their vehicle on the eating trip and then pick it back up after the second work trip.

Therefore, for each tour there is an initial choice of *tour mode*, which applies for the entire tour and constrains the modes available for each trip on that tour. Then, for each of these trips the *trip mode* is chosen upon departing for the trip, given the constraints posed by the chosen tour mode.

In BEAM the following **trip modes** are considered:

* Walk
* Bike
* Drive (alone)
* Drive (alone or with passengers)
* Private car passenger (only allowed if defined in input plans)
* Walk to Transit
* Drive to Transit (Park and Ride)
* Bike to Transit (Park and Ride)
* Ride Hail (Solo, Pooled)
* Ride Hail to/from Transit

And the following **tour modes** are considered, with the trip modes that are allowed under that tour mode

* Walk based
* Walk
* Private car passenger
* Walk to Transit
* Drive / bike to Transit (only for first and last legs of tour)
* Ride Hail (Solo, Pooled)
* Ride Hail to/from Transit
* Drive and Bike (only if they use shared vehicles)
* Car based
* Drive (alone or with passengers)
* Bike based
* Bike

When an agent starts a day without pre-chosen trip and tour modes, they make their first choices when they are departing on their first trip of the day. They first estimate the utility of taking each trip of their upcoming tour via every available mode, using the same utility equations used in the trip mode choice model (see below). The utilities for the first trip are taken from the beam router using the vehicles (shared and private) available to the agent at the time of their departure, and the utilities for the remaining trips are estimated from the skims. For each trip in the tour, the utilities are grouped into tour modes based on which modes are allowed by each tour mode, and the expected maximum utility is calculated for each tour mode for each trip by taking the logsum of the utilities for the available modes. The total expected utility of a tour mode is taken by summing the maximum expected utilities of each trip given that tour mode. The tour mode is chosen via a multinomial logit over the expected utilities for the three tour modes.

Once the tour mode is chosen, it is stored for the remainder of the tour, and the agent completes a trip mode choice process each time they depart on a trip (including immediately after making their initial tour mode choice). In some cases, a tour mode choice also involves choosing a specific personal vehicle. This is most apparent for ``CAR_BASED`` and ``BIKE_BASED`` tour modes, which involve choosing the vehicle that is taken along on the tour and must be returned home at the end of the day. In addition, ``WALK_BASED`` tours can be assigned personal vehicles if a personal vehicle is used for access to transit in the first trip of the tour (for instance, a multimodal park and ride trip). In that case, the vehicle remains parked at a transit station and needs to be returned home on the last trip of the day by a multimodal transit trip, with the vehicle being used for the egress rather than access portion of the trip). Currently, BEAM does not support multimodal trips with personal vehicles in the middle of tours rather than as the first and last legs.

In all cases (whether mode is specified before the day or chosen within the day) person agents use WALK as a fallback option throughout if constraints otherwise prevent their previously determined mode from being possible for any given trip. E.g. if a person is in the middle of a RIDE_HAIL tour, but the Ride Hail Manager is unable to match a driver to the person, then the person will walk.

There are two mode choice models that are possible within BEAM.

Multinomial Logit Mode Choice
Expand Down Expand Up @@ -51,6 +98,8 @@ The ASC (alternative specific constant) parameters as well as the Beta parameter
Latent Class Mode Choice
~~~~~~~~~~~~~~~~~~~~~~~~

This method is no longer being actively updated.

Parking
-------

Expand Down
2 changes: 1 addition & 1 deletion docs/inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ Routing Configuration
bike_rent = 180
walk = 0
car = 300
ride_hail = 0
ride_hail = 30
}
}
gh.useAlternativeRoutes = false
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ As an alternative you could use a prepared beam `docker <https://www.docker.com/
In order to be able to modify scenario config files we need to export the scenario directory to the host file system.
To do it execute the following commands in an empty directory::

docker create --name tmp_beam beammodel/beam:0.9.12
docker create --name tmp_beam beammodel/beam:1.0.0
docker cp tmp_beam:/app/test ./

Urbansim SF-light Scenario
Expand All @@ -21,7 +21,7 @@ directory name that includes datetime would be different.

In case you are using docker you need to run the following command::

docker run --rm -v ./output:/app/output -v ./test:/app/test -e JAVA_OPTS='-Xmx10g' beammodel/beam:0.9.12 --config test/input/sf-light/sf-light-urbansim-5k-hh.conf
docker run --rm -v ./output:/app/output -v ./test:/app/test -e JAVA_OPTS='-Xmx10g' beammodel/beam:1.0.0 --config test/input/sf-light/sf-light-urbansim-5k-hh.conf


Urbansim SF-light Scenario with mode choice in Beam
Expand Down
2 changes: 1 addition & 1 deletion production/austin
Submodule austin updated 576 files
2 changes: 1 addition & 1 deletion production/newyork
Submodule newyork updated 101 files
2 changes: 1 addition & 1 deletion production/seattle
Submodule seattle updated 79 files
+0 −0 beamFuelTypes.csv
+0 −0 csvInput/households.csv
+0 −0 csvInput/plans.csv
+0 −0 csvInput/population.csv
+0 −0 csvInput/vehicles.csv
+0 −4,382 freight/2024-04-11/Baseline/freight-merged-carriers.csv
+0 −25,284 freight/2024-04-11/Baseline/freight-merged-payload-plans.csv
+0 −4,382 freight/2024-04-11/Baseline/freight-merged-tours.csv
+0 −9,535 freight/2024-04-11/Baseline_30pct/freight-merged-carriers.csv
+0 −49,662 freight/2024-04-11/Baseline_30pct/freight-merged-payload-plans.csv
+0 −9,535 freight/2024-04-11/Baseline_30pct/freight-merged-tours.csv
+0 −8,645 freight/2024-04-20/Baseline/freight-merged-carriers.csv
+0 −45,891 freight/2024-04-20/Baseline/freight-merged-payload-plans.csv
+0 −8,645 freight/2024-04-20/Baseline/freight-merged-tours.csv
+0 −0 freight/freight-carriers.csv
+0 −0 freight/freight-depots.csv.gz
+0 −0 freight/freight-tours.csv
+0 −0 freight/payload-plans.csv
+0 −0 population-100.csv
+0 −0 r5-dense/clipped_tazs.csv
+ r5-dense/kitsap_transit.zip
+0 −0 r5-dense/linkToGradePercent.csv
+0 −0 r5-dense/seattle-unclassified-simplified-unprojected.osm.pbf
+ r5-dense/seattle_gtfs.zip
+0 −0 r5/clipped_tazs.csv
+ r5/kitsap_transit.zip
+0 −0 r5/linkToGradePercent.csv
+ r5/r5-partially-simplified/fares.dat
+ r5/r5-partially-simplified/tolls.dat
+0 −3 r5/r5-unclassified-simplified/0.linkstats.seattle-base-newcalibration-20230927.csv.gz
+0 −2,645 r5/r5-unsimplified/clipped_tazs.csv
+ r5/r5-unsimplified/fares.dat
+ r5/r5-unsimplified/kitsap_transit.zip
+0 −95,717 r5/r5-unsimplified/linkToGradePercent.csv
+ r5/r5-unsimplified/seattle_gtfs.zip
+ r5/r5-unsimplified/tolls.dat
+0 −0 r5/seattle-partially-simplified.osm.pbf
+0 −0 r5/seattle-unsimplified.osm.pbf
+ r5/seattle_gtfs.zip
+0 −0 rideHailFleet.csv
+0 −0 rideHailFleet.csv.gz
+0 −501 samples/rideHailFleet-fenced.csv
+0 −20 scenarios/freight/seattle-freight-20240411-Baseline-30pct.conf
+0 −20 scenarios/freight/seattle-freight-20240411-Baseline.conf
+0 −20 scenarios/freight/seattle-freight-20240420-Baseline.conf
+2 −1 seattle-1k.conf
+0 −1,175 seattle-base.conf
+0 −0 seattle-calibration/benchmark.csv
+0 −0 seattle-calibration/experiment-test.yml
+0 −0 seattle-calibration/experiment.yml
+0 −25 seattle-pilates-base.conf
+96 −28 seattle.conf
+0 −0 taz-parking-limited.csv.gz
+0 −0 taz-parking.csv.gz
+3 −0 urbansim-2.5k/blocks.csv.gz
+3 −0 urbansim-2.5k/households.csv.gz
+3 −0 urbansim-2.5k/persons.csv.gz
+3 −0 urbansim-2.5k/plans.csv.gz
+0 −0 vehicle-energy/default-energy-primary.csv
+0 −0 vehicle-energy/default-energy-secondary.csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_6_Box_truck_(BEV,_2025,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_6_Box_truck_(Diesel,_2020,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_6_Box_truck_(HEV,_2025,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Box_truck_(BEV,_2025,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Box_truck_(Diesel,_2020,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Box_truck_(HEV,_2025,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Sleeper_cab_high_roof_(BEV,_2025,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Sleeper_cab_high_roof_(Diesel,_2020,_no_program).csv
+0 −44,001 vehicle-tech/Freight_Baseline_FASTSimData_2020/Class_8_Sleeper_cab_high_roof_(HEV,_2025,_no_program).csv
+0 −13 vehicle-tech/freight-only-vehicletypes--Baseline.csv
+0 −0 vehicleTypes-cavs-all.csv
+0 −0 vehicleTypes-cavs-half.csv
+0 −0 vehicleTypes-personal-evs.csv
+0 −0 vehicleTypes-rh-cavs.csv
+0 −0 vehicleTypes-rh-ev-humans.csv
+0 −0 vehicleTypes-test-energy.csv
+5 −5 vehicleTypes.csv
+340,700 −0 vehicles.csv
+0 −0 vehicles.csv.gz
2 changes: 1 addition & 1 deletion production/sfbay
Submodule sfbay updated 6474 files
172 changes: 172 additions & 0 deletions src/main/java/beam/agentsim/events/TourModeChoiceEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package beam.agentsim.events;

import beam.agentsim.agents.modalbehaviors.DrivesVehicle;
import beam.agentsim.agents.planning.Tour;
import beam.agentsim.agents.planning.Trip;
import beam.router.Modes;
import beam.router.TourModes;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.events.Event;
import org.matsim.api.core.v01.population.Activity;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.api.internal.HasPersonId;
import scala.collection.JavaConverters;
import scala.collection.Seq;
import scala.collection.immutable.Vector;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* BEAM
*/



public class TourModeChoiceEvent extends Event implements HasPersonId {
public final static String EVENT_TYPE = "TourModeChoice";
public final static String ATTRIBUTE_PERSON_ID = "person";

public final static String ATTRIBUTE_TOUR_MODE = "tourMode";
public final static String ATTRIBUTE_MODE_TO_TOUR_MODE = "modeToTourMode";
public final static String ATTRIBUTE_AVAILABLE_VEHICLES = "availableVehicles";
public final static String ATTRIBUTE_TOUR_ACTIVITIES = "tourActivities";
public final static String ATTRIBUTE_AVAILABLE_MODES = "availableModes";
public final static String ATTRIBUTE_TOUR_MODE_UTILITY = "tourModeUtility";
public final static String ATTRIBUTE_CURRENT_ACTIVITY = "startActivity";
public final static String ATTRIBUTE_CURRENT_ACTIVITY_X = "startX";
public final static String ATTRIBUTE_CURRENT_ACTIVITY_Y = "startY";

public final Id<Person> personId;
public final String tourMode;
public final Tour currentTour;
public final String modeToTourModeString;
public final String availablePersonalStreetVehiclesString;
public final String tourActivitiesString;
public final String availableModesString;
public final String tourModeToUtilityString;
public final String startActivityType;
public final Double startX;
public final Double startY;



public TourModeChoiceEvent(double time,
Id<Person> personId,
String tourMode,
Tour currentTour,
Vector<DrivesVehicle.VehicleOrToken> availablePersonalStreetVehicles,
scala.collection.immutable.Map<TourModes.BeamTourMode, Seq<Modes.BeamMode>> modeToTourMode,
scala.collection.immutable.Map<TourModes.BeamTourMode, scala.Double> tourModeUtils,
Vector<Modes.BeamMode> availableModes,
Activity startActivity) {
this(time,
personId,
tourMode,
currentTour,
modeToTourMode.mkString("-"),
stringifyVehicles(availablePersonalStreetVehicles),
currentTour == null ? "" : stringifyActivities(currentTour),
availableModes.mkString("-"),
tourModeUtils.mkString("; "),
startActivity.getType(),
startActivity.getCoord().getX(),
startActivity.getCoord().getY());
}

public TourModeChoiceEvent(double time,
Id<Person> personId,
String tourMode,
Tour currentTour,
String modeToTourModeString,
String availablePersonalStreetVehiclesString,
String tourActivitiesString,
String availableModesString,
String tourModeToUtilityString,
String startActivityType,
Double startX,
Double startY) {
super(time);

this.personId = personId;
this.tourMode = tourMode;
this.currentTour = currentTour;
this.modeToTourModeString = modeToTourModeString;
this.availablePersonalStreetVehiclesString = availablePersonalStreetVehiclesString;
this.tourActivitiesString = tourActivitiesString;
this.availableModesString = availableModesString;
this.tourModeToUtilityString = tourModeToUtilityString;
this.startActivityType = startActivityType;
this.startX = startX;
this.startY = startY;
}

public static TourModeChoiceEvent apply(Event event) {
if (!(event instanceof TourModeChoiceEvent) && EVENT_TYPE.equalsIgnoreCase(event.getEventType())) {
Map<String, String> attr = event.getAttributes();
return new TourModeChoiceEvent(event.getTime(),
Id.createPersonId(attr.get(ATTRIBUTE_PERSON_ID)),
attr.get(ATTRIBUTE_TOUR_MODE),
null,
attr.get(ATTRIBUTE_MODE_TO_TOUR_MODE),
attr.get(ATTRIBUTE_AVAILABLE_VEHICLES),
attr.get(ATTRIBUTE_TOUR_ACTIVITIES),
attr.get(ATTRIBUTE_AVAILABLE_MODES),
attr.get(ATTRIBUTE_TOUR_MODE_UTILITY),
attr.get(ATTRIBUTE_CURRENT_ACTIVITY),
Double.parseDouble(attr.get(ATTRIBUTE_CURRENT_ACTIVITY_Y)),
Double.parseDouble(attr.get(ATTRIBUTE_CURRENT_ACTIVITY_X))
);
}
return (TourModeChoiceEvent) event;
}


@Override
public Map<String, String> getAttributes() {
Map<String, String> attr = super.getAttributes();
attr.put(ATTRIBUTE_PERSON_ID, personId.toString());
attr.put(ATTRIBUTE_TOUR_MODE, tourMode);
attr.put(ATTRIBUTE_MODE_TO_TOUR_MODE, modeToTourModeString);
attr.put(ATTRIBUTE_AVAILABLE_VEHICLES, availablePersonalStreetVehiclesString);
attr.put(ATTRIBUTE_TOUR_ACTIVITIES, tourActivitiesString);
attr.put(ATTRIBUTE_AVAILABLE_MODES, availableModesString);
attr.put(ATTRIBUTE_TOUR_MODE_UTILITY, tourModeToUtilityString);
attr.put(ATTRIBUTE_CURRENT_ACTIVITY, startActivityType);
attr.put(ATTRIBUTE_CURRENT_ACTIVITY_X, Double.toString(startX));
attr.put(ATTRIBUTE_CURRENT_ACTIVITY_Y, Double.toString(startY));
return attr;
}

@Override
public String getEventType() {
return EVENT_TYPE;
}

@Override
public Id<Person> getPersonId() {
return personId;
}


public static String stringifyVehicles(Vector<DrivesVehicle.VehicleOrToken> vehicles) {
List<String> out = new ArrayList<>();
List<DrivesVehicle.VehicleOrToken> javaVehicles = JavaConverters.seqAsJavaList(vehicles.toSeq());
for (DrivesVehicle.VehicleOrToken veh : javaVehicles) {
out.add(veh.vehicle().beamVehicleType().toString());
}
return String.join("-", out);
}

public static String stringifyActivities(Tour tour) {
List<String> out = new ArrayList<>();
List<Activity> javaActivities = JavaConverters.seqAsJavaList(tour.activities().toSeq());
for (Activity act : javaActivities) {
out.add(act.getType());
}
return String.join("->", out);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ private void overrideDefaultLoggerSetup(String eventsToWrite) {
case "ModeChoiceEvent":
eventClass = ModeChoiceEvent.class;
break;
case "TourModeChoiceEvent":
eventClass = TourModeChoiceEvent.class;
break;
case "ParkingEvent":
eventClass = ParkingEvent.class;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,14 @@ private Leg createLeg(PathTraversalEvent pte, Leg connectedLeg, Integer departur

List<Object> objects = pte.linkIdsJava();
// most of the time the last link of previous leg is the first link of current leg - we are avoiding this
boolean sameLinkAtTheEnd = !linkIds.isEmpty()
&& pte.linkIds().head().toString().equals(Iterables.getLast(linkIds).toString());
boolean sameLinkAtTheEnd;
try {
sameLinkAtTheEnd = !linkIds.isEmpty()
&& pte.linkIds().head().toString().equals(Iterables.getLast(linkIds).toString());
} catch (java.util.NoSuchElementException e) {
log.error("Mismatched path traversal in physsim plans: {}, matched leg: {}", pte, connectedLeg);
return null;
}
for (int i = sameLinkAtTheEnd ? 1 : 0; i < objects.size(); i++) {
Object linkObjId = objects.get(i);
Id<Link> linkId = Id.createLinkId(linkObjId.toString());
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/beam-template.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1052,9 +1052,9 @@ beam.routing {
accessBufferTimeSeconds {
bike = "int | 60"
bike_rent = "int | 180"
walk = "int | 0"
walk = "int | 1"
car = "int | 300"
ride_hail = "int | 0"
ride_hail = "int | 30"
}
}
gh {
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/beam/agentsim/agents/MobilityRequest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ object MobilityRequest {
person,
act,
-1,
Trip(act, None, new Tour()),
Trip(act, None, new Tour(0)),
BeamMode.CAR,
requestType,
-1,
Expand Down
Loading

0 comments on commit 274e00d

Please sign in to comment.