From c40970fa66a58d6b28bd122a0433f1f715235e4f Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Fri, 25 Feb 2022 11:13:54 +0300 Subject: [PATCH 01/40] currentTripMode vs currentTourMode --- .../beam/agentsim/agents/PersonAgent.scala | 103 ++++-- .../agents/modalbehaviors/ChoosesMode.scala | 302 +++++++----------- .../agents/modalbehaviors/DrivesVehicle.scala | 22 +- src/main/scala/beam/router/Modes.scala | 1 + .../plan/sampling/AvailableModeUtils.scala | 4 + 5 files changed, 200 insertions(+), 232 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 2170f710f79..69e2072f265 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -189,6 +189,7 @@ object PersonAgent { restOfCurrentTrip: List[EmbodiedBeamLeg] = List(), currentVehicle: VehicleStack = Vector(), currentTourMode: Option[BeamMode] = None, + currentTripMode: Option[BeamMode] = None, currentTourPersonalVehicle: Option[Id[BeamVehicle]] = None, passengerSchedule: PassengerSchedule = PassengerSchedule(), currentLegPassengerScheduleIndex: Int = 0, @@ -475,6 +476,46 @@ class PersonAgent( } } + def isFirstTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, _) = currentTripIndexWithinTour(personData, nextAct) + tripIndexOfElement == 0 + } + + def isLastTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) + tripIndexOfElement == lastTripIndex + } + + def isFirstOrLastTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) + tripIndexOfElement == 0 || tripIndexOfElement == lastTripIndex + } + + def currentTripIndexWithinTour(personData: BasePersonData, nextAct: Activity): (Int, Int) = { + val tour = currentTour(personData) + val lastTripIndex = tour.trips.size - 1 + val tripIndexOfElement = tour + .tripIndexOfElement(nextAct) + .getOrElse(throw new IllegalArgumentException(s"Element [$nextAct] not found")) + if (tripIndexOfElement == 0 && currentActivity(personData).getType != "Home") + logger.warn( + "~Activity {}, idx {}, id {}, next {}", + currentActivity(personData).getType, + tripIndexOfElement, + id, + nextAct.getType + ) + if (tripIndexOfElement == lastTripIndex && nextAct.getType != "Home") + logger.warn( + "Cur ~Activity {}, idx {}, id {}, next {}", + currentActivity(personData).getType, + tripIndexOfElement, + id, + nextAct.getType + ) + (tripIndexOfElement, lastTripIndex) + } + def currentActivity(data: BasePersonData): Activity = _experiencedBeamPlan.activities(data.currentActivityIndex) @@ -587,15 +628,10 @@ class PersonAgent( val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data.copy( - // If the mode of the next leg is defined and is CAV, use it, otherwise, - // If we don't have a current tour mode (i.e. are not on a tour aka at home), - // use the mode of the next leg as the new tour mode. - currentTourMode = modeOfNextLeg match { - case Some(CAV) => - Some(CAV) - case _ => - data.currentTourMode.orElse(modeOfNextLeg) - }, + // We do not stick to current tour mode + // If we have the currentTourPersonalVehicle then we should use it + // use the mode of the next leg as the new trip mode. + currentTripMode = modeOfNextLeg, numberOfReplanningAttempts = 0, failedTrips = IndexedSeq.empty, enrouteData = EnrouteData() @@ -612,7 +648,7 @@ class PersonAgent( when(Teleporting) { case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -626,7 +662,7 @@ class PersonAgent( case Event( TriggerWithId(TeleportationEndsTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, maybeCurrentTourMode, _, _, _, true, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, maybeCurrentTripMode, _, _, _, true, _, _, _, _, _) ) => holdTickAndTriggerId(tick, triggerId) @@ -639,7 +675,7 @@ class PersonAgent( startY = currentTrip.legs.head.beamLeg.travelPath.startPoint.loc.getY, endX = currentTrip.legs.last.beamLeg.travelPath.endPoint.loc.getX, endY = currentTrip.legs.last.beamLeg.travelPath.endPoint.loc.getY, - currentTourMode = maybeCurrentTourMode.map(_.value) + currentTourMode = maybeCurrentTripMode.map(_.value) ) eventsManager.processEvent(teleportationEvent) @@ -654,7 +690,7 @@ class PersonAgent( */ case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -663,7 +699,7 @@ class PersonAgent( case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, true, _, _, _, _, _) + BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, _, true, _, _, _, _, _) ) => // We're coming back from replanning, i.e. we are already on the trip, so we don't throw a departure event logDebug(s"replanned to leg ${restOfCurrentTrip.head}") @@ -719,7 +755,7 @@ class PersonAgent( val currentCoord = beamServices.geo.wgs2Utm(data.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( - data.copy(currentTourMode = None, numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), + data.copy(currentTripMode = None, numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), currentLocation = SpaceTime( currentCoord, tick @@ -738,7 +774,7 @@ class PersonAgent( // TRANSIT FAILURE case Event( ReservationResponse(Left(firstErrorResponse), _), - data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"replanning because ${firstErrorResponse.errorCode}") @@ -812,7 +848,7 @@ class PersonAgent( // RIDE HAIL FAILURE case Event( response @ RideHailResponse(_, _, Some(error), _, _), - data @ BasePersonData(_, _, _, _, _, _, _, _, _, _, _, _, _, _) + data: BasePersonData ) => handleFailedRideHailReservation(error, response, data) } @@ -823,7 +859,7 @@ class PersonAgent( */ case Event( TriggerWithId(BoardVehicleTrigger(tick, vehicleToEnter), triggerId), - data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"PersonEntersVehicle: $vehicleToEnter @ $tick") eventsManager.processEvent(new PersonEntersVehicleEvent(tick, id, vehicleToEnter)) @@ -856,7 +892,7 @@ class PersonAgent( */ case Event( TriggerWithId(AlightVehicleTrigger(tick, vehicleToExit, energyConsumedOption), triggerId), - data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) if vehicleToExit.equals(currentVehicle.head) => updateFuelConsumed(energyConsumedOption) logDebug(s"PersonLeavesVehicle: $vehicleToExit @ $tick") @@ -933,6 +969,7 @@ class PersonAgent( _, _, _, + _, currentCost, _, _, @@ -977,6 +1014,7 @@ class PersonAgent( goto(ChoosingMode) using ChoosesModeData( basePersonData.copy( currentTourMode = None, // Have to give up my mode as well, perhaps there's no option left for driving. + currentTripMode = None, currentTourPersonalVehicle = None, numberOfReplanningAttempts = basePersonData.numberOfReplanningAttempts + 1 ), @@ -1073,6 +1111,7 @@ class PersonAgent( _, _, _, + _, _ ) ) if nextLeg.asDriver => @@ -1167,7 +1206,7 @@ class PersonAgent( nextState // TRANSIT but too late - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit && nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the bus. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1182,7 +1221,7 @@ class PersonAgent( val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data - .copy(currentTourMode = Some(WALK_TRANSIT), numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), + .copy(currentTripMode = Some(WALK_TRANSIT), numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), currentLocation = SpaceTime(currentCoord, _currentTick.get), isWithinTripReplanning = true, excludeModes = @@ -1190,7 +1229,7 @@ class PersonAgent( else Vector(BeamMode.RIDE_HAIL, BeamMode.CAR, BeamMode.CAV) ) // TRANSIT - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit => val resRequest = TransitReservationRequest( nextLeg.beamLeg.travelPath.transitStops.get.fromIdx, @@ -1201,7 +1240,7 @@ class PersonAgent( TransitDriverAgent.selectByVehicleId(nextLeg.beamVehicleId) ! resRequest goto(WaitingForReservationConfirmation) // RIDE_HAIL - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.isRideHail => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) @@ -1229,7 +1268,7 @@ class PersonAgent( goto(WaitingForReservationConfirmation) // CAV but too late // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the CAV. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1244,7 +1283,7 @@ class PersonAgent( val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data - .copy(currentTourMode = Some(WALK_TRANSIT), numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), + .copy(currentTripMode = Some(WALK_TRANSIT), numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), currentLocation = SpaceTime(currentCoord, _currentTick.get), isWithinTripReplanning = true, excludeModes = @@ -1253,7 +1292,7 @@ class PersonAgent( ) // CAV // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) => + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) val resRequest = ReservationRequest( legSegment.head.beamLeg, @@ -1273,7 +1312,8 @@ class PersonAgent( _, _, _, - currentTourMode @ Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION), + _, + Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION), _, _, _, @@ -1313,7 +1353,8 @@ class PersonAgent( currentTrip = None, restOfCurrentTrip = List(), currentTourPersonalVehicle = None, - currentTourMode = if (activity.getType.equals("Home")) None else currentTourMode, + currentTourMode = if (activity.getType.equals("Home")) None else data.currentTourMode, + currentTripMode = None, hasDeparted = false ) case None => @@ -1331,6 +1372,7 @@ class PersonAgent( _, _, currentTourMode, + _, currentTourPersonalVehicle, _, _, @@ -1413,6 +1455,7 @@ class PersonAgent( None }, currentTourMode = if (activity.getType.equals("Home")) None else currentTourMode, + currentTripMode = None, hasDeparted = false ) case None => @@ -1486,7 +1529,7 @@ class PersonAgent( } def getReplanningReasonFrom(data: BasePersonData, prefix: String): String = { - data.currentTourMode + data.currentTripMode .collect { case mode => s"$prefix $mode" } @@ -1560,7 +1603,7 @@ class PersonAgent( handleBoardOrAlightOutOfPlace case Event( TriggerWithId(BoardVehicleTrigger(_, vehicleId), triggerId), - BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _) + BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) if currentVehicle.nonEmpty && currentVehicle.head.equals(vehicleId) => log.debug("Person {} in state {} received Board for vehicle that he is already on, ignoring...", id, stateName) stay() replying CompletionNotice(triggerId, Vector()) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 46d369da40a..fef8fbbb4c6 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -128,102 +128,22 @@ trait ChoosesMode { def bodyVehiclePersonId: PersonIdWithActorRef = PersonIdWithActorRef(id, self) - def currentTourBeamVehicle: Option[BeamVehicle] = { - stateData match { - case data: ChoosesModeData => - data.personData.currentTourPersonalVehicle match { - case Some(personalVehicle) => - Option( - beamVehicles(personalVehicle) - .asInstanceOf[ActualVehicle] - .vehicle - ) - case _ => None - } - case data: BasePersonData => - data.currentTourPersonalVehicle match { - case Some(personalVehicle) => - Option( - beamVehicles(personalVehicle) - .asInstanceOf[ActualVehicle] - .vehicle - ) - case _ => None - } - case _ => - None - } - } - onTransition { case _ -> ChoosingMode => nextStateData match { // If I am already on a tour in a vehicle, only that vehicle is available to me - case ChoosesModeData( - BasePersonData(_, _, _, _, _, Some(vehicle), _, _, _, _, _, _, _, _), - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _ + case data: ChoosesModeData + if data.personData.currentTourPersonalVehicle.isDefined && ( + data.personData.currentTourMode.exists(mode => mode == CAR || mode == BIKE) + || data.personData.currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) + && isLastTripWithinTour(data.personData, nextActivity(data.personData).get) ) => - self ! MobilityStatusResponse(Vector(beamVehicles(vehicle)), getCurrentTriggerIdOrGenerate) - // Only need to get available street vehicles if our mode requires such a vehicle - case ChoosesModeData( - BasePersonData( - _, - _, - _, - _, - Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION), - _, - _, - _, - _, - _, - _, - _, - _, - _ - ), - currentLocation, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _ - ) => - val teleportationVehicle = createSharedTeleportationVehicle(currentLocation) + self ! MobilityStatusResponse( + Vector(beamVehicles(data.personData.currentTourPersonalVehicle.get)), + getCurrentTriggerIdOrGenerate + ) + // Create teleportation vehicle if we are told to use teleportation + case data: ChoosesModeData if data.personData.currentTripMode.exists(_.isHovTeleportation) => + val teleportationVehicle = createSharedTeleportationVehicle(data.currentLocation) val vehicles = Vector(ActualVehicle(teleportationVehicle)) self ! MobilityStatusResponse(vehicles, getCurrentTriggerIdOrGenerate) // Only need to get available street vehicles if our mode requires such a vehicle @@ -242,6 +162,7 @@ trait ChoosesMode { _, _, _, + _, _ ), currentLocation, @@ -314,37 +235,22 @@ trait ChoosesMode { case Event(MobilityStatusResponse(newlyAvailableBeamVehicles, triggerId), choosesModeData: ChoosesModeData) => beamVehicles ++= newlyAvailableBeamVehicles.map(v => v.id -> v) val currentPersonLocation = choosesModeData.currentLocation - val availableModes: Seq[BeamMode] = availableModesForPerson( - matsimPlan.getPerson - ).filterNot(mode => choosesModeData.excludeModes.contains(mode)) + val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) + val personData = choosesModeData.personData + val nextAct = nextActivity(personData).get // Make sure the current mode is allowable - val replanningIsAvailable = - choosesModeData.personData.numberOfReplanningAttempts < beamServices.beamConfig.beam.agentsim.agents.modalBehaviors.maximumNumberOfReplanningAttempts - val correctedCurrentTourMode = choosesModeData.personData.currentTourMode match { - case Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)) - if availableModes.contains(CAR) && replanningIsAvailable => - Some(mode) - case Some(mode) if availableModes.contains(mode) && replanningIsAvailable => Some(mode) - case Some(mode) if availableModes.contains(mode) => Some(WALK) - case None if !replanningIsAvailable => Some(WALK) - case _ => None - } + val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules(personData, nextAct, availableModes) val bodyStreetVehicle = createBodyStreetVehicle(currentPersonLocation) - val nextAct = nextActivity(choosesModeData.personData).get val departTime = _currentTick.get var availablePersonalStreetVehicles = - correctedCurrentTourMode match { + correctedCurrentTripMode match { case None | Some(CAR | BIKE | HOV2_TELEPORTATION | HOV3_TELEPORTATION) => // In these cases, a personal vehicle will be involved newlyAvailableBeamVehicles case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => - val tour = _experiencedBeamPlan.getTourContaining(nextAct) - val tripIndexOfElement = tour - .tripIndexOfElement(nextAct) - .getOrElse(throw new IllegalArgumentException(s"Element [$nextAct] not found")) - if (tripIndexOfElement == 0 || tripIndexOfElement == tour.trips.size - 1) { + if (isFirstOrLastTripWithinTour(personData, nextAct)) { newlyAvailableBeamVehicles } else { Vector() @@ -411,7 +317,7 @@ trait ChoosesMode { streetVehicles: Vector[StreetVehicle], byMode: BeamMode ): Vector[StreetVehicle] = { - choosesModeData.personData.currentTourPersonalVehicle match { + personData.currentTourPersonalVehicle match { case Some(personalVeh) => // We already have a vehicle we're using on this tour, so filter down to that streetVehicles.filter(_.id == personalVeh) @@ -427,7 +333,7 @@ trait ChoosesMode { var requestId: Option[Int] = None // Form and send requests - correctedCurrentTourMode match { + correctedCurrentTripMode match { case None => if (hasRideHail) { responsePlaceholders = makeResponsePlaceholders( @@ -456,7 +362,7 @@ trait ChoosesMode { makeRequestWith(withTransit = true, Vector(bodyStreetVehicle)) case Some(CAV) => // Request from household the trip legs to put into trip - householdRef ! CavTripLegsRequest(bodyVehiclePersonId, currentActivity(choosesModeData.personData)) + householdRef ! CavTripLegsRequest(bodyVehiclePersonId, currentActivity(personData)) responsePlaceholders = makeResponsePlaceholders(withPrivateCAV = true) case Some(HOV2_TELEPORTATION) => val vehicles = filterStreetVehiclesForQuery(newlyAvailableBeamVehicles.map(_.streetVehicle), CAR) @@ -508,13 +414,10 @@ trait ChoosesMode { } case Some(mode @ (DRIVE_TRANSIT | BIKE_TRANSIT)) => val vehicleMode = Modes.getAccessVehicleMode(mode) - val LastTripIndex = currentTour(choosesModeData.personData).trips.size - 1 - val tripIndexOfElement = currentTour(choosesModeData.personData) - .tripIndexOfElement(nextAct) - .getOrElse(throw new IllegalArgumentException(s"Element [$nextAct] not found")) + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) ( tripIndexOfElement, - choosesModeData.personData.currentTourPersonalVehicle + personData.currentTourPersonalVehicle ) match { case (0, _) if !choosesModeData.isWithinTripReplanning => // We use our car if we are not replanning, otherwise we end up doing a walk transit (catch-all below) @@ -526,7 +429,7 @@ trait ChoosesMode { :+ bodyStreetVehicle ) responsePlaceholders = makeResponsePlaceholders(withRouting = true) - case (LastTripIndex, Some(currentTourPersonalVehicle)) => + case (`lastTripIndex`, Some(currentTourPersonalVehicle)) => // At the end of the tour, only drive home a vehicle that we have also taken away from there. makeRequestWith( withTransit = true, @@ -562,7 +465,7 @@ trait ChoosesMode { logDebug(m.toString) } val newPersonData = choosesModeData.copy( - personData = choosesModeData.personData.copy(currentTourMode = correctedCurrentTourMode), + personData = personData.copy(currentTripMode = correctedCurrentTripMode), availablePersonalStreetVehicles = availablePersonalStreetVehicles, allAvailableStreetVehicles = newlyAvailableBeamVehicles, routingResponse = responsePlaceholders.routingResponse, @@ -799,6 +702,28 @@ trait ChoosesMode { ) } using completeChoiceIfReady) + private def correctCurrentTripModeAccordingToRules( + personData: BasePersonData, + nextAct: Activity, + availableModes: Seq[BeamMode] + ): Option[BeamMode] = { + val replanningIsAvailable = + personData.numberOfReplanningAttempts < beamServices.beamConfig.beam.agentsim.agents.modalBehaviors.maximumNumberOfReplanningAttempts + (personData.currentTripMode, personData.currentTourMode) match { + case (_, Some(CAR | BIKE)) if personData.currentTourPersonalVehicle.isDefined => personData.currentTourMode + case (_, Some(DRIVE_TRANSIT | BIKE_TRANSIT)) + if personData.currentTourPersonalVehicle.isDefined && isLastTripWithinTour(personData, nextAct) => + personData.currentTourMode + case (Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)), _) + if availableModes.contains(CAR) && replanningIsAvailable => + Some(mode) + case (Some(mode), _) if availableModes.contains(mode) && replanningIsAvailable => Some(mode) + case (Some(mode), _) if availableModes.contains(mode) => Some(WALK) + case (None, _) if !replanningIsAvailable => Some(WALK) + case _ => None + } + } + private def makeParkingInquiries( choosesModeData: ChoosesModeData, itineraries: Seq[EmbodiedBeamTrip] @@ -1182,7 +1107,7 @@ trait ChoosesMode { ) <= beamScenario.beamConfig.beam.agentsim.agents.rideHail.allocationManager.maxWaitingTimeInSec => val origLegs = travelProposal.toEmbodiedBeamLegsForCustomer(bodyVehiclePersonId) (travelProposal.poolingInfo match { - case Some(poolingInfo) if !choosesModeData.personData.currentTourMode.contains(RIDE_HAIL) => + case Some(poolingInfo) if !choosesModeData.personData.currentTripMode.contains(RIDE_HAIL) => val pooledLegs = origLegs.map { origLeg => origLeg.copy( cost = origLeg.cost * poolingInfo.costFactor, @@ -1224,7 +1149,7 @@ trait ChoosesMode { def isAvailable(mode: BeamMode): Boolean = combinedItinerariesForChoice.exists(_.tripClassifier == mode) - choosesModeData.personData.currentTourMode match { + choosesModeData.personData.currentTripMode match { case Some(expectedMode) if expectedMode.isTransit && !isAvailable(expectedMode) => eventsManager.processEvent( createFailedTransitODSkimmerEvent(currentPersonLocation.loc, nextAct.getCoord, expectedMode) @@ -1232,20 +1157,13 @@ trait ChoosesMode { case _ => } - val availableModesForTrips: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson) - .filterNot(mode => choosesModeData.excludeModes.contains(mode)) + val availableModesForTrips: Seq[BeamMode] = + availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) - val filteredItinerariesForChoice = (choosesModeData.personData.currentTourMode match { + val filteredItinerariesForChoice = (choosesModeData.personData.currentTripMode match { case Some(mode) if mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT => - val LastTripIndex = currentTour(choosesModeData.personData).trips.size - 1 - val tripIndexOfElement = currentTour(choosesModeData.personData) - .tripIndexOfElement(nextAct) - .getOrElse(throw new IllegalArgumentException(s"Element [$nextAct] not found")) - ( - tripIndexOfElement, - personData.hasDeparted - ) match { - case (0 | LastTripIndex, false) => + (isFirstOrLastTripWithinTour(personData, nextAct), personData.hasDeparted) match { + case (true, false) => combinedItinerariesForChoice.filter(_.tripClassifier == mode) case _ => combinedItinerariesForChoice.filter(trip => @@ -1272,6 +1190,13 @@ trait ChoosesMode { .asInstanceOf[AttributesOfIndividual] val availableAlts = Some(filteredItinerariesForChoice.map(_.tripClassifier).mkString(":")) + def gotoFinishingModeChoice(chosenTrip: EmbodiedBeamTrip) = { + goto(FinishingModeChoice) using choosesModeData.copy( + pendingChosenTrip = Some(chosenTrip), + availableAlternatives = availableAlts + ) + } + modeChoiceCalculator( filteredItinerariesForChoice, attributesOfIndividual, @@ -1279,13 +1204,9 @@ trait ChoosesMode { Some(matsimPlan.getPerson) ) match { case Some(chosenTrip) => - val dataForNextStep = choosesModeData.copy( - pendingChosenTrip = Some(chosenTrip), - availableAlternatives = availableAlts - ) - goto(FinishingModeChoice) using dataForNextStep + gotoFinishingModeChoice(chosenTrip) case None => - choosesModeData.personData.currentTourMode match { + choosesModeData.personData.currentTripMode match { case Some(CAV) => // Special case, if you are using household CAV, no choice was necessary you just use this mode // Construct the embodied trip to allow for processing by FinishingModeChoice and scoring @@ -1307,10 +1228,7 @@ trait ChoosesMode { body.beamVehicleType.id ) val cavTrip = EmbodiedBeamTrip(walk1 +: cavTripLegs.legs.toVector :+ walk2) - goto(FinishingModeChoice) using choosesModeData.copy( - pendingChosenTrip = Some(cavTrip), - availableAlternatives = availableAlts - ) + gotoFinishingModeChoice(cavTrip) } else { val bushwhackingTrip = RoutingWorker.createBushwackingTrip( choosesModeData.currentLocation.loc, @@ -1319,49 +1237,61 @@ trait ChoosesMode { body.toStreetVehicle, geo ) - goto(FinishingModeChoice) using choosesModeData.copy( - pendingChosenTrip = Some(bushwhackingTrip), - availableAlternatives = availableAlts - ) + gotoFinishingModeChoice(bushwhackingTrip) } case Some(_) => - //give another chance to make a choice without predefined mode - self ! MobilityStatusResponse(choosesModeData.allAvailableStreetVehicles, getCurrentTriggerId.get) - stay() using ChoosesModeData( - personData = personData.copy(currentTourMode = None), - currentLocation = choosesModeData.currentLocation, - excludeModes = choosesModeData.excludeModes - ) + val correctedTripMode = + correctCurrentTripModeAccordingToRules(personData, nextAct, availableModesForTrips) + if (correctedTripMode != personData.currentTripMode) { + //give another chance to make a choice without predefined mode + gotoChoosingModeWithoutPredefinedMode(choosesModeData) + } else { + val expensiveWalkTrip = createExpensiveWalkTrip(currentPersonLocation, nextAct, routingResponse) + gotoFinishingModeChoice(expensiveWalkTrip) + } case _ => // Bad things happen but we want them to continue their day, so we signal to downstream that trip should be made to be expensive - val originalWalkTripLeg = - routingResponse.itineraries.find(_.tripClassifier == WALK) match { - case Some(originalWalkTrip) => - originalWalkTrip.legs.head - case None => - RoutingWorker - .createBushwackingTrip( - currentPersonLocation.loc, - nextAct.getCoord, - _currentTick.get, - body.toStreetVehicle, - beamServices.geo - ) - .legs - .head - } - val expensiveWalkTrip = EmbodiedBeamTrip( - Vector(originalWalkTripLeg.copy(replanningPenalty = 10.0)) - ) - - goto(FinishingModeChoice) using choosesModeData.copy( - pendingChosenTrip = Some(expensiveWalkTrip), - availableAlternatives = availableAlts - ) + val expensiveWalkTrip = createExpensiveWalkTrip(currentPersonLocation, nextAct, routingResponse) + gotoFinishingModeChoice(expensiveWalkTrip) } } } + private def createExpensiveWalkTrip( + currentPersonLocation: SpaceTime, + nextAct: Activity, + routingResponse: RoutingResponse + ) = { + val originalWalkTripLeg = + routingResponse.itineraries.find(_.tripClassifier == WALK) match { + case Some(originalWalkTrip) => + originalWalkTrip.legs.head + case None => + RoutingWorker + .createBushwackingTrip( + currentPersonLocation.loc, + nextAct.getCoord, + _currentTick.get, + body.toStreetVehicle, + beamServices.geo + ) + .legs + .head + } + val expensiveWalkTrip = EmbodiedBeamTrip( + Vector(originalWalkTripLeg.copy(replanningPenalty = 10.0)) + ) + expensiveWalkTrip + } + + private def gotoChoosingModeWithoutPredefinedMode(choosesModeData: ChoosesModeData) = { + goto(ChoosingMode) using choosesModeData.copy( + personData = choosesModeData.personData.copy(currentTripMode = None), + currentLocation = choosesModeData.currentLocation, + excludeModes = choosesModeData.excludeModes + ) + } + private def createFailedTransitODSkimmerEvent( originLocation: Location, destinationLocation: Location, @@ -1470,7 +1400,7 @@ trait ChoosesMode { tick, id, chosenTrip.tripClassifier.value, - data.personData.currentTourMode.map(_.value).getOrElse(""), + data.personData.currentTripMode.map(_.value).getOrElse(""), data.expectedMaxUtilityOfLatestChoice.getOrElse[Double](Double.NaN), _experiencedBeamPlan .activities(data.personData.currentActivityIndex) @@ -1486,7 +1416,7 @@ trait ChoosesMode { ) eventsManager.processEvent(modeChoiceEvent) - data.personData.currentTourMode match { + data.personData.currentTripMode match { case Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION) => scheduler ! CompletionNotice( triggerId, @@ -1533,8 +1463,8 @@ trait ChoosesMode { goto(WaitingForDeparture) using data.personData.copy( currentTrip = Some(chosenTrip), restOfCurrentTrip = chosenTrip.legs.toList, - currentTourMode = data.personData.currentTourMode - .orElse(Some(chosenTrip.tripClassifier)), + currentTourMode = data.personData.currentTourMode.orElse(Some(chosenTrip.tripClassifier)), + currentTripMode = data.personData.currentTripMode.orElse(Some(chosenTrip.tripClassifier)), currentTourPersonalVehicle = if (isCurrentPersonalVehicleVoided) vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala index 6ed9d63cb33..da9c4b84921 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala @@ -222,7 +222,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon case Event( TriggerWithId(EndLegTrigger(tick), triggerId), LiterallyDrivingData(data: BasePersonData, _, _) - ) if data.currentTourMode.contains(HOV2_TELEPORTATION) || data.currentTourMode.contains(HOV3_TELEPORTATION) => + ) if data.currentTripMode.contains(HOV2_TELEPORTATION) || data.currentTourMode.contains(HOV3_TELEPORTATION) => updateLatestObservedTick(tick) val dataForNextLegOrActivity: BasePersonData = data.copy( @@ -324,7 +324,6 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon tollsAccumulated += tollOnCurrentLeg val riders = data.passengerSchedule.schedule(currentLeg).riders.toIndexedSeq.map(_.personId) val numberOfPassengers: Int = calculateNumberOfPassengersBasedOnCurrentTourMode(data, currentLeg, riders) - val currentTourMode: Option[String] = getCurrentTourMode(data) val pte = PathTraversalEvent( tick, currentVehicleUnderControl, @@ -332,7 +331,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon currentBeamVehicle.beamVehicleType, numberOfPassengers, currentLeg, - currentTourMode, + getCurrentTripMode(data), fuelConsumed.primaryFuel, fuelConsumed.secondaryFuel, currentBeamVehicle.primaryFuelLevelInJoules, @@ -482,16 +481,8 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon stay() } - private def getCurrentTourMode(data: DrivingData): Option[String] = { - data match { - case bpd: BasePersonData => - bpd.currentTourMode match { - case Some(mode: BeamMode) => Some(mode.value) - case _ => None - } - case _ => None - } - } + private def getCurrentTripMode(data: DrivingData): Option[String] = + findPersonData(data).flatMap(_.currentTripMode).map(_.value) private def calculateNumberOfPassengersBasedOnCurrentTourMode( data: DrivingData, @@ -500,7 +491,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon ): Int = { val numberOfPassengers = data match { case bpd: BasePersonData => - (bpd.currentTourMode, currentLeg.mode) match { + (bpd.currentTripMode, currentLeg.mode) match { // can't directly check HOV2/3 because the equals in BeamMode is overridden case (Some(mode @ BeamMode.CAR), BeamMode.CAR) if mode.value == BeamMode.CAR_HOV2.value => riders.size + 1 case (Some(mode @ BeamMode.CAR), BeamMode.CAR) if mode.value == BeamMode.CAR_HOV3.value => riders.size + 2 @@ -540,7 +531,6 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon tollsAccumulated += tollOnCurrentLeg val numberOfPassengers: Int = calculateNumberOfPassengersBasedOnCurrentTourMode(data, partiallyCompletedBeamLeg, riders) - val currentTourMode: Option[String] = getCurrentTourMode(data) val pte = PathTraversalEvent( updatedStopTick, currentVehicleUnderControl, @@ -548,7 +538,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon currentBeamVehicle.beamVehicleType, numberOfPassengers, partiallyCompletedBeamLeg, - currentTourMode, + getCurrentTripMode(data), fuelConsumed.primaryFuel, fuelConsumed.secondaryFuel, currentBeamVehicle.primaryFuelLevelInJoules, diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index aa73937123f..c602f6e4f08 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -34,6 +34,7 @@ object Modes { def isTransit: Boolean = isR5TransitMode(this) def isMassTransit: Boolean = this == SUBWAY || this == RAIL || this == FERRY || this == TRAM def isRideHail: Boolean = this == RIDE_HAIL + def isHovTeleportation: Boolean = this == HOV2_TELEPORTATION || this == HOV3_TELEPORTATION } object BeamMode extends StringEnum[BeamMode] with StringCirceEnum[BeamMode] { diff --git a/src/main/scala/beam/utils/plan/sampling/AvailableModeUtils.scala b/src/main/scala/beam/utils/plan/sampling/AvailableModeUtils.scala index 74e96c718aa..6218ba63057 100644 --- a/src/main/scala/beam/utils/plan/sampling/AvailableModeUtils.scala +++ b/src/main/scala/beam/utils/plan/sampling/AvailableModeUtils.scala @@ -60,6 +60,10 @@ object AvailableModeUtils extends LazyLogging { getPersonCustomAttributes(person).map(_.availableModes).getOrElse(Seq.empty) } + def availableModesForPerson(person: Person, excludedModes: Seq[BeamMode]): Seq[BeamMode] = { + getPersonCustomAttributes(person).map(_.availableModes).getOrElse(Seq.empty).filterNot(excludedModes.contains) + } + /** * Sets the available modes for the given person in the population * From 4d2e37552a6bc43664f08fb211176f53ada86e30 Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Fri, 25 Feb 2022 12:16:05 +0300 Subject: [PATCH 02/40] Provide personal vehicle only when agent is at home --- .../beam/agentsim/agents/PersonAgent.scala | 26 +++++-------------- .../agents/household/HouseholdActor.scala | 4 +-- .../household/HouseholdFleetManager.scala | 11 +++++--- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 69e2072f265..5df05df283d 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -286,6 +286,8 @@ object PersonAgent { case basePersonData: BasePersonData => Some(basePersonData) case _ => None } + + def atHome(activity: Activity): Boolean = activity.getType.equalsIgnoreCase("home") } class PersonAgent( @@ -407,7 +409,7 @@ class PersonAgent( // which is used in place of our real remaining tour distance of 0.0 // which should help encourage residential end-of-day charging val tomorrowFirstLegDistance = - if (nextAct.getType.toLowerCase == "home") { + if (atHome(nextAct)) { findFirstCarLegOfTrip(personData) match { case Some(carLeg) => carLeg.beamLeg.travelPath.distanceInM @@ -497,22 +499,6 @@ class PersonAgent( val tripIndexOfElement = tour .tripIndexOfElement(nextAct) .getOrElse(throw new IllegalArgumentException(s"Element [$nextAct] not found")) - if (tripIndexOfElement == 0 && currentActivity(personData).getType != "Home") - logger.warn( - "~Activity {}, idx {}, id {}, next {}", - currentActivity(personData).getType, - tripIndexOfElement, - id, - nextAct.getType - ) - if (tripIndexOfElement == lastTripIndex && nextAct.getType != "Home") - logger.warn( - "Cur ~Activity {}, idx {}, id {}, next {}", - currentActivity(personData).getType, - tripIndexOfElement, - id, - nextAct.getType - ) (tripIndexOfElement, lastTripIndex) } @@ -1353,7 +1339,7 @@ class PersonAgent( currentTrip = None, restOfCurrentTrip = List(), currentTourPersonalVehicle = None, - currentTourMode = if (activity.getType.equals("Home")) None else data.currentTourMode, + currentTourMode = if (atHome(activity)) None else data.currentTourMode, currentTripMode = None, hasDeparted = false ) @@ -1443,7 +1429,7 @@ class PersonAgent( currentTourPersonalVehicle = currentTourPersonalVehicle match { case Some(personalVehId) => val personalVeh = beamVehicles(personalVehId).asInstanceOf[ActualVehicle].vehicle - if (activity.getType.equals("Home")) { + if (atHome(activity)) { potentiallyChargingBeamVehicles.put(personalVeh.id, beamVehicles(personalVeh.id)) beamVehicles -= personalVeh.id personalVeh.getManager.get ! ReleaseVehicle(personalVeh, triggerId) @@ -1454,7 +1440,7 @@ class PersonAgent( case None => None }, - currentTourMode = if (activity.getType.equals("Home")) None else currentTourMode, + currentTourMode = if (atHome(activity)) None else currentTourMode, currentTripMode = None, hasDeparted = false ) diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala index 1c25958ce19..1af1da550b8 100755 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala @@ -197,8 +197,8 @@ object HouseholdActor { val homeCoordFromPlans = household.members .flatMap(person => person.getSelectedPlan.getPlanElements.asScala.flatMap { - case act: Activity if act.getType == "Home" => Some(act.getCoord) - case _ => None + case act: Activity if PersonAgent.atHome(act) => Some(act.getCoord) + case _ => None } ) .headOption diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala index ebd7062290b..a0caffb9bad 100644 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala @@ -7,6 +7,7 @@ import akka.util.Timeout import beam.agentsim.Resource.NotifyVehicleIdle import beam.agentsim.agents.BeamAgent.Finish import beam.agentsim.agents.InitializeTrigger +import beam.agentsim.agents.PersonAgent.atHome import beam.agentsim.agents.household.HouseholdActor._ import beam.agentsim.agents.household.HouseholdFleetManager.ResolvedParkingResponses import beam.agentsim.agents.modalbehaviors.DrivesVehicle.ActualVehicle @@ -104,7 +105,7 @@ class HouseholdFleetManager( case GetVehicleTypes(triggerId) => sender() ! VehicleTypesResponse(vehicles.values.map(_.beamVehicleType).toSet, triggerId) - case MobilityStatusInquiry(personId, whenWhere, _, requireVehicleCategoryAvailable, triggerId) => + case MobilityStatusInquiry(personId, whenWhere, originActivity, requireVehicleCategoryAvailable, triggerId) => { for { neededVehicleCategory <- requireVehicleCategoryAvailable @@ -157,13 +158,15 @@ class HouseholdFleetManager( } }.getOrElse { availableVehicles = availableVehicles match { - case firstVehicle :: rest => + //in case of replanning because of TRANSIT failure WALK_TRANSIT is used + //but we may want to introduce maxWalkingDistance and check that the agent is close enough to the vehicle + case firstVehicle :: rest if atHome(originActivity) => logger.debug("Vehicle {} is now taken", firstVehicle.id) firstVehicle.becomeDriver(sender) sender() ! MobilityStatusResponse(Vector(ActualVehicle(firstVehicle)), triggerId) rest - case Nil => - logger.debug(s"Not returning vehicle because no default is defined") + case _ => + logger.debug(s"Not returning vehicle because no default is defined or agent is not at home") sender() ! MobilityStatusResponse(Vector(), triggerId) Nil } From a58eef515fc6fb879767e45efd2305d7c72be42b Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Wed, 23 Mar 2022 10:47:49 +0300 Subject: [PATCH 03/40] Introduced personalVehicleModes --- .../agentsim/agents/modalbehaviors/ChoosesMode.scala | 9 ++++----- src/main/scala/beam/router/Modes.scala | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 335ad00111f..deae736128a 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -133,8 +133,7 @@ trait ChoosesMode { // If I am already on a tour in a vehicle, only that vehicle is available to me case data: ChoosesModeData if data.personData.currentTourPersonalVehicle.isDefined && ( - data.personData.currentTourMode.exists(mode => mode == CAR || mode == BIKE) - || data.personData.currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) + data.personData.currentTourMode.exists(mode => Modes.isPersonalVehicleMode(mode)) && isLastTripWithinTour(data.personData, nextActivity(data.personData).get) ) => self ! MobilityStatusResponse( @@ -153,7 +152,7 @@ trait ChoosesMode { _, _, _, - plansModeOption @ (None | Some(CAR | BIKE | DRIVE_TRANSIT | BIKE_TRANSIT)), + plansModeOption, _, _, _, @@ -186,7 +185,7 @@ trait ChoosesMode { _, _, _ - ) => + ) if plansModeOption.forall(Modes.isPersonalVehicleMode) => implicit val executionContext: ExecutionContext = context.system.dispatcher plansModeOption match { case Some(CAR | DRIVE_TRANSIT) => @@ -1035,7 +1034,7 @@ trait ChoosesMode { ): Seq[EmbodiedBeamTrip] = { itineraries.map { itin => itin.tripClassifier match { - case CAR | DRIVE_TRANSIT | BIKE_TRANSIT | BIKE => + case mode if Modes.isPersonalVehicleMode(mode) => // find parking legs (the subsequent leg of the same vehicle) val parkingLegs = itin.legs.zip(itin.legs.tail).collect { case (leg1, leg2) if leg1.beamVehicleId == leg2.beamVehicleId && legVehicleHasParkingBehavior(leg2) => leg2 diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index c602f6e4f08..c610f65a285 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -125,6 +125,8 @@ object Modes { val chainBasedModes = Seq(CAR, BIKE) + val personalVehicleModes = Seq(CAR, BIKE, DRIVE_TRANSIT, BIKE_TRANSIT) + val transitModes = Seq(BUS, FUNICULAR, GONDOLA, CABLE_CAR, FERRY, TRAM, TRANSIT, RAIL, SUBWAY) @@ -162,6 +164,8 @@ object Modes { def isChainBasedMode(beamMode: BeamMode): Boolean = BeamMode.chainBasedModes.contains(beamMode) + def isPersonalVehicleMode(beamMode: BeamMode): Boolean = BeamMode.personalVehicleModes.contains(beamMode) + implicit def beamMode2R5Mode(beamMode: BeamMode): Either[LegMode, TransitModes] = beamMode.r5Mode.get From 93566b68e3a94f942ddd1e9b278f9dd8a3e0867d Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Wed, 23 Mar 2022 15:45:23 +0300 Subject: [PATCH 04/40] Better handling of situation when we do the second mode choice --- .../beam/agentsim/agents/modalbehaviors/ChoosesMode.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index deae736128a..e9fa0d32ec1 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -256,7 +256,8 @@ trait ChoosesMode { val personData = choosesModeData.personData val nextAct = nextActivity(personData).get // Make sure the current mode is allowable - val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules(personData, nextAct, availableModes) + val correctedCurrentTripMode = + correctCurrentTripModeAccordingToRules(personData.currentTripMode, personData, nextAct, availableModes) val bodyStreetVehicle = createBodyStreetVehicle(currentPersonLocation) val departTime = _currentTick.get @@ -723,13 +724,14 @@ trait ChoosesMode { } using completeChoiceIfReady) private def correctCurrentTripModeAccordingToRules( + currentTripMode: Option[BeamMode], personData: BasePersonData, nextAct: Activity, availableModes: Seq[BeamMode] ): Option[BeamMode] = { val replanningIsAvailable = personData.numberOfReplanningAttempts < beamServices.beamConfig.beam.agentsim.agents.modalBehaviors.maximumNumberOfReplanningAttempts - (personData.currentTripMode, personData.currentTourMode) match { + (currentTripMode, personData.currentTourMode) match { case (_, Some(CAR | BIKE)) if personData.currentTourPersonalVehicle.isDefined => personData.currentTourMode case (_, Some(DRIVE_TRANSIT | BIKE_TRANSIT)) if personData.currentTourPersonalVehicle.isDefined && isLastTripWithinTour(personData, nextAct) => @@ -1261,7 +1263,7 @@ trait ChoosesMode { } case Some(_) => val correctedTripMode = - correctCurrentTripModeAccordingToRules(personData, nextAct, availableModesForTrips) + correctCurrentTripModeAccordingToRules(None, personData, nextAct, availableModesForTrips) if (correctedTripMode != personData.currentTripMode) { //give another chance to make a choice without predefined mode gotoChoosingModeWithoutPredefinedMode(choosesModeData) From 691456f648c493cd5092f41159b38611bc29d3a6 Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Wed, 23 Mar 2022 16:47:08 +0300 Subject: [PATCH 05/40] Using currentTripMode to request available vehicles --- .../agents/modalbehaviors/ChoosesMode.scala | 55 +++---------------- 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index e9fa0d32ec1..90ba006b69e 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -146,67 +146,28 @@ trait ChoosesMode { val vehicles = Vector(ActualVehicle(teleportationVehicle)) self ! MobilityStatusResponse(vehicles, getCurrentTriggerIdOrGenerate) // Only need to get available street vehicles if our mode requires such a vehicle - case ChoosesModeData( - BasePersonData( - currentActivityIndex, - _, - _, - _, - plansModeOption, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _ - ), - currentLocation, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _, - _ - ) if plansModeOption.forall(Modes.isPersonalVehicleMode) => + case data: ChoosesModeData if data.personData.currentTripMode.forall(Modes.isPersonalVehicleMode) => implicit val executionContext: ExecutionContext = context.system.dispatcher - plansModeOption match { + data.personData.currentTripMode match { case Some(CAR | DRIVE_TRANSIT) => requestAvailableVehicles( vehicleFleets, - currentLocation, - _experiencedBeamPlan.activities(currentActivityIndex), + data.currentLocation, + _experiencedBeamPlan.activities(data.personData.currentActivityIndex), Some(VehicleCategory.Car) ) pipeTo self case Some(BIKE | BIKE_TRANSIT) => requestAvailableVehicles( vehicleFleets, - currentLocation, - _experiencedBeamPlan.activities(currentActivityIndex), + data.currentLocation, + _experiencedBeamPlan.activities(data.personData.currentActivityIndex), Some(VehicleCategory.Bike) ) pipeTo self case _ => requestAvailableVehicles( vehicleFleets, - currentLocation, - _experiencedBeamPlan.activities(currentActivityIndex) + data.currentLocation, + _experiencedBeamPlan.activities(data.personData.currentActivityIndex) ) pipeTo self } From 6d063a9fe572f5cbce7fd668f5f55c98eb28a8ed Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Thu, 24 Mar 2022 12:40:36 +0300 Subject: [PATCH 06/40] Using correctedCurrentTripMode when getting available vehicles. Correct trip mode for teleport modes. --- .../agents/modalbehaviors/ChoosesMode.scala | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 90ba006b69e..cb492f44d8c 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -129,6 +129,18 @@ trait ChoosesMode { def bodyVehiclePersonId: PersonIdWithActorRef = PersonIdWithActorRef(id, self) onTransition { case _ -> ChoosingMode => + val correctedCurrentTripMode = nextStateData match { + case choosesModeData: ChoosesModeData => + val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) + val nextAct = nextActivity(choosesModeData.personData).get + correctCurrentTripModeAccordingToRules( + choosesModeData.personData.currentTripMode, + choosesModeData.personData, + nextAct, + availableModes + ) + case _ => None + } nextStateData match { // If I am already on a tour in a vehicle, only that vehicle is available to me case data: ChoosesModeData @@ -141,14 +153,14 @@ trait ChoosesMode { getCurrentTriggerIdOrGenerate ) // Create teleportation vehicle if we are told to use teleportation - case data: ChoosesModeData if data.personData.currentTripMode.exists(_.isHovTeleportation) => + case data: ChoosesModeData if correctedCurrentTripMode.exists(_.isHovTeleportation) => val teleportationVehicle = createSharedTeleportationVehicle(data.currentLocation) val vehicles = Vector(ActualVehicle(teleportationVehicle)) self ! MobilityStatusResponse(vehicles, getCurrentTriggerIdOrGenerate) // Only need to get available street vehicles if our mode requires such a vehicle - case data: ChoosesModeData if data.personData.currentTripMode.forall(Modes.isPersonalVehicleMode) => + case data: ChoosesModeData if correctedCurrentTripMode.forall(Modes.isPersonalVehicleMode) => implicit val executionContext: ExecutionContext = context.system.dispatcher - data.personData.currentTripMode match { + correctedCurrentTripMode match { case Some(CAR | DRIVE_TRANSIT) => requestAvailableVehicles( vehicleFleets, @@ -700,6 +712,9 @@ trait ChoosesMode { case (Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)), _) if availableModes.contains(CAR) && replanningIsAvailable => Some(mode) + case (Some(CAR_HOV2 | CAR_HOV3), Some(tourMode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION))) + if availableModes.contains(CAR) && replanningIsAvailable => + Some(tourMode) case (Some(mode), _) if availableModes.contains(mode) && replanningIsAvailable => Some(mode) case (Some(mode), _) if availableModes.contains(mode) => Some(WALK) case (None, _) if !replanningIsAvailable => Some(WALK) @@ -1415,6 +1430,7 @@ trait ChoosesMode { goto(Teleporting) using data.personData.copy( currentTrip = Some(chosenTrip), + currentTourMode = data.personData.currentTripMode, restOfCurrentTrip = List() ) From de1684a4dd732f7ca0e8aad7598eabad785c092e Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Fri, 25 Mar 2022 11:45:57 +0300 Subject: [PATCH 07/40] Fixed not using current tour personal vehicle. Fixed trip/tour field. --- .../agents/modalbehaviors/ChoosesMode.scala | 37 +++++++++---------- .../agents/modalbehaviors/DrivesVehicle.scala | 2 +- .../beam/integration/SingleModeSpec.scala | 8 ++-- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index cb492f44d8c..9307b6537ab 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -129,25 +129,24 @@ trait ChoosesMode { def bodyVehiclePersonId: PersonIdWithActorRef = PersonIdWithActorRef(id, self) onTransition { case _ -> ChoosingMode => - val correctedCurrentTripMode = nextStateData match { - case choosesModeData: ChoosesModeData => - val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) - val nextAct = nextActivity(choosesModeData.personData).get - correctCurrentTripModeAccordingToRules( - choosesModeData.personData.currentTripMode, - choosesModeData.personData, - nextAct, - availableModes - ) - case _ => None - } + val choosesModeData: ChoosesModeData = nextStateData.asInstanceOf[ChoosesModeData] + val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) + val nextAct = nextActivity(choosesModeData.personData).get + val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( + choosesModeData.personData.currentTripMode, + choosesModeData.personData, + nextAct, + availableModes + ) nextStateData match { // If I am already on a tour in a vehicle, only that vehicle is available to me case data: ChoosesModeData - if data.personData.currentTourPersonalVehicle.isDefined && ( - data.personData.currentTourMode.exists(mode => Modes.isPersonalVehicleMode(mode)) - && isLastTripWithinTour(data.personData, nextActivity(data.personData).get) - ) => + if data.personData.currentTourPersonalVehicle.isDefined && + ( + data.personData.currentTourMode.exists(mode => mode == CAR || mode == BIKE) || + data.personData.currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) + && isLastTripWithinTour(data.personData, nextAct) + ) => self ! MobilityStatusResponse( Vector(beamVehicles(data.personData.currentTourPersonalVehicle.get)), getCurrentTriggerIdOrGenerate @@ -165,21 +164,21 @@ trait ChoosesMode { requestAvailableVehicles( vehicleFleets, data.currentLocation, - _experiencedBeamPlan.activities(data.personData.currentActivityIndex), + currentActivity(data.personData), Some(VehicleCategory.Car) ) pipeTo self case Some(BIKE | BIKE_TRANSIT) => requestAvailableVehicles( vehicleFleets, data.currentLocation, - _experiencedBeamPlan.activities(data.personData.currentActivityIndex), + currentActivity(data.personData), Some(VehicleCategory.Bike) ) pipeTo self case _ => requestAvailableVehicles( vehicleFleets, data.currentLocation, - _experiencedBeamPlan.activities(data.personData.currentActivityIndex) + currentActivity(data.personData) ) pipeTo self } diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala index dbcb1c91bce..dc19f806e0b 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala @@ -222,7 +222,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash with Expon case Event( TriggerWithId(EndLegTrigger(tick), triggerId), LiterallyDrivingData(data: BasePersonData, _, _) - ) if data.currentTripMode.contains(HOV2_TELEPORTATION) || data.currentTourMode.contains(HOV3_TELEPORTATION) => + ) if data.currentTripMode.contains(HOV2_TELEPORTATION) || data.currentTripMode.contains(HOV3_TELEPORTATION) => updateLatestObservedTick(tick) val dataForNextLegOrActivity: BasePersonData = data.copy( diff --git a/src/test/scala/beam/integration/SingleModeSpec.scala b/src/test/scala/beam/integration/SingleModeSpec.scala index f29744f7c47..0764c304fbe 100755 --- a/src/test/scala/beam/integration/SingleModeSpec.scala +++ b/src/test/scala/beam/integration/SingleModeSpec.scala @@ -11,7 +11,7 @@ import beam.router.RouteHistory import beam.sflight.RouterForTest import beam.sim.common.GeoUtilsImpl import beam.sim.{BeamHelper, BeamMobsim, RideHailFleetInitializerProvider} -import beam.utils.SimRunnerForTest +import beam.utils.{MathUtils, SimRunnerForTest} import beam.utils.TestConfigUtils.testConfig import com.typesafe.config.ConfigFactory import org.matsim.api.core.v01.events.{ActivityEndEvent, Event, PersonDepartureEvent, PersonEntersVehicleEvent} @@ -269,8 +269,10 @@ class SingleModeSpec val personDepartureEvents = events.collect { case event: PersonDepartureEvent => event } personDepartureEvents should not be empty val regularPersonEvents = filterOutProfessionalDriversAndCavs(personDepartureEvents) - val (drive, others) = regularPersonEvents.map(_.getLegMode).partition(_ == "car") - others.size should be < (0.02 * drive.size).toInt + val othersCount = regularPersonEvents.count(_.getLegMode != "car") + withClue("Majority of agents should use cars. Other modes take place when no car available.") { + othersCount should be < MathUtils.doubleToInt(0.02 * regularPersonEvents.size) + } } } From 8a0dacda2827a65b441a9159cb3f324141fa971a Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Mon, 28 Mar 2022 18:49:17 +0300 Subject: [PATCH 08/40] Keep track of current tour/trip mode in _experiencedBeamPlan. Moved method atHome to BeamPlan. --- .../beam/agentsim/agents/PersonAgent.scala | 48 +++++++---------- .../agents/household/HouseholdActor.scala | 5 +- .../household/HouseholdFleetManager.scala | 2 +- .../agents/modalbehaviors/ChoosesMode.scala | 53 ++++++++++++++----- .../agentsim/agents/planning/BeamPlan.scala | 39 ++++++++++---- .../agentsim/agents/planning/Strategy.scala | 4 +- .../beam/agentsim/planning/BeamPlanSpec.scala | 26 ++++----- .../beam/integration/SingleModeSpec.scala | 10 ++-- 8 files changed, 117 insertions(+), 70 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 1df2f842243..3c612bc427b 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -17,6 +17,8 @@ import beam.agentsim.agents.parking.ChoosesParking.{ ChoosingParkingSpot, ReleasingParkingSpot } +import beam.agentsim.agents.planning.BeamPlan.atHome +import beam.agentsim.agents.planning.Strategy.ModeChoiceStrategy import beam.agentsim.agents.planning.{BeamPlan, Tour} import beam.agentsim.agents.ridehail.RideHailManager.TravelProposal import beam.agentsim.agents.ridehail._ @@ -188,7 +190,6 @@ object PersonAgent { currentTrip: Option[EmbodiedBeamTrip] = None, restOfCurrentTrip: List[EmbodiedBeamLeg] = List(), currentVehicle: VehicleStack = Vector(), - currentTourMode: Option[BeamMode] = None, currentTripMode: Option[BeamMode] = None, currentTourPersonalVehicle: Option[Id[BeamVehicle]] = None, passengerSchedule: PassengerSchedule = PassengerSchedule(), @@ -286,8 +287,6 @@ object PersonAgent { case basePersonData: BasePersonData => Some(basePersonData) case _ => None } - - def atHome(activity: Activity): Boolean = activity.getType.equalsIgnoreCase("home") } class PersonAgent( @@ -604,16 +603,12 @@ class PersonAgent( case Some(nextAct) => logDebug(s"wants to go to ${nextAct.getType} @ $tick") holdTickAndTriggerId(tick, triggerId) - val indexOfNextActivity = _experiencedBeamPlan.getPlanElements.indexOf(nextAct) - val modeOfNextLeg = _experiencedBeamPlan.getPlanElements.get(indexOfNextActivity - 1) match { - case leg: Leg => BeamMode.fromString(leg.getMode) - case _ => None - } + val modeOfNextLeg = _experiencedBeamPlan.getTripStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) val currentCoord = currentActivity(data).getCoord val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data.copy( - // We do not stick to current tour mode + // We current tour mode is defined in _experiencedBeamPlan.getTourStrategy // If we have the currentTourPersonalVehicle then we should use it // use the mode of the next leg as the new trip mode. currentTripMode = modeOfNextLeg, @@ -633,7 +628,7 @@ class PersonAgent( when(Teleporting) { case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -647,7 +642,7 @@ class PersonAgent( case Event( TriggerWithId(TeleportationEndsTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, maybeCurrentTripMode, _, _, _, true, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, maybeCurrentTripMode, _, _, _, true, _, _, _, _, _) ) => holdTickAndTriggerId(tick, triggerId) @@ -679,7 +674,7 @@ class PersonAgent( */ case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -688,7 +683,7 @@ class PersonAgent( case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, _, true, _, _, _, _, _) + BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, true, _, _, _, _, _) ) => // We're coming back from replanning, i.e. we are already on the trip, so we don't throw a departure event logDebug(s"replanned to leg ${restOfCurrentTrip.head}") @@ -763,7 +758,7 @@ class PersonAgent( // TRANSIT FAILURE case Event( ReservationResponse(Left(firstErrorResponse), _), - data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"replanning because ${firstErrorResponse.errorCode}") @@ -848,7 +843,7 @@ class PersonAgent( */ case Event( TriggerWithId(BoardVehicleTrigger(tick, vehicleToEnter), triggerId), - data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"PersonEntersVehicle: $vehicleToEnter @ $tick") eventsManager.processEvent(new PersonEntersVehicleEvent(tick, id, vehicleToEnter)) @@ -881,7 +876,7 @@ class PersonAgent( */ case Event( TriggerWithId(AlightVehicleTrigger(tick, vehicleToExit, energyConsumedOption), triggerId), - data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _) ) if vehicleToExit.equals(currentVehicle.head) => updateFuelConsumed(energyConsumedOption) logDebug(s"PersonLeavesVehicle: $vehicleToExit @ $tick") @@ -958,7 +953,6 @@ class PersonAgent( _, _, _, - _, currentCost, _, _, @@ -1000,9 +994,10 @@ class PersonAgent( val currentCoord = beamServices.geo.wgs2Utm(basePersonData.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc val nextCoord = nextActivity(basePersonData).get.getCoord + // Have to give up my mode as well, perhaps there's no option left for driving. + _experiencedBeamPlan.putStrategy(currentTour(basePersonData), ModeChoiceStrategy(mode = None)) goto(ChoosingMode) using ChoosesModeData( basePersonData.copy( - currentTourMode = None, // Have to give up my mode as well, perhaps there's no option left for driving. currentTripMode = None, currentTourPersonalVehicle = None, numberOfReplanningAttempts = basePersonData.numberOfReplanningAttempts + 1 @@ -1100,7 +1095,6 @@ class PersonAgent( _, _, _, - _, _ ) ) if nextLeg.asDriver => @@ -1195,7 +1189,7 @@ class PersonAgent( nextState // TRANSIT but too late - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit && nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the bus. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1218,7 +1212,7 @@ class PersonAgent( else Vector(BeamMode.RIDE_HAIL, BeamMode.CAR, BeamMode.CAV) ) // TRANSIT - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit => val resRequest = TransitReservationRequest( nextLeg.beamLeg.travelPath.transitStops.get.fromIdx, @@ -1229,7 +1223,7 @@ class PersonAgent( TransitDriverAgent.selectByVehicleId(nextLeg.beamVehicleId) ! resRequest goto(WaitingForReservationConfirmation) // RIDE_HAIL - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.isRideHail => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) @@ -1257,7 +1251,7 @@ class PersonAgent( goto(WaitingForReservationConfirmation) // CAV but too late // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the CAV. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1281,7 +1275,7 @@ class PersonAgent( ) // CAV // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) => + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) val resRequest = ReservationRequest( legSegment.head.beamLeg, @@ -1301,7 +1295,6 @@ class PersonAgent( _, _, _, - _, Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION), _, _, @@ -1342,7 +1335,6 @@ class PersonAgent( currentTrip = None, restOfCurrentTrip = List(), currentTourPersonalVehicle = None, - currentTourMode = if (atHome(activity)) None else data.currentTourMode, currentTripMode = None, hasDeparted = false ) @@ -1360,7 +1352,6 @@ class PersonAgent( Some(currentTrip), _, _, - currentTourMode, _, currentTourPersonalVehicle, _, @@ -1443,7 +1434,6 @@ class PersonAgent( case None => None }, - currentTourMode = if (atHome(activity)) None else currentTourMode, currentTripMode = None, hasDeparted = false ) @@ -1592,7 +1582,7 @@ class PersonAgent( handleBoardOrAlightOutOfPlace case Event( TriggerWithId(BoardVehicleTrigger(_, vehicleId), triggerId), - BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) + BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _) ) if currentVehicle.nonEmpty && currentVehicle.head.equals(vehicleId) => log.debug("Person {} in state {} received Board for vehicle that he is already on, ignoring...", id, stateName) stay() replying CompletionNotice(triggerId, Vector()) diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala index 1af1da550b8..b8fdf8dc9b0 100755 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala @@ -11,6 +11,7 @@ import beam.agentsim.agents.modalbehaviors.ChoosesMode.{CavTripLegsRequest, CavT import beam.agentsim.agents.modalbehaviors.DrivesVehicle.VehicleOrToken import beam.agentsim.agents.modalbehaviors.ModeChoiceCalculator import beam.agentsim.agents.planning.BeamPlan +import beam.agentsim.agents.planning.BeamPlan.atHome import beam.agentsim.agents.ridehail.RideHailAgent.{ ModifyPassengerSchedule, ModifyPassengerScheduleAck, @@ -197,8 +198,8 @@ object HouseholdActor { val homeCoordFromPlans = household.members .flatMap(person => person.getSelectedPlan.getPlanElements.asScala.flatMap { - case act: Activity if PersonAgent.atHome(act) => Some(act.getCoord) - case _ => None + case act: Activity if atHome(act) => Some(act.getCoord) + case _ => None } ) .headOption diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala index a0caffb9bad..1f39666be49 100644 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala @@ -7,10 +7,10 @@ import akka.util.Timeout import beam.agentsim.Resource.NotifyVehicleIdle import beam.agentsim.agents.BeamAgent.Finish import beam.agentsim.agents.InitializeTrigger -import beam.agentsim.agents.PersonAgent.atHome import beam.agentsim.agents.household.HouseholdActor._ import beam.agentsim.agents.household.HouseholdFleetManager.ResolvedParkingResponses import beam.agentsim.agents.modalbehaviors.DrivesVehicle.ActualVehicle +import beam.agentsim.agents.planning.BeamPlan.atHome import beam.agentsim.agents.vehicles.BeamVehicle import beam.agentsim.events.SpaceTime import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse} diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 9307b6537ab..5e10e467afc 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -8,6 +8,7 @@ import beam.agentsim.agents._ import beam.agentsim.agents.household.HouseholdActor.{MobilityStatusInquiry, MobilityStatusResponse, ReleaseVehicle} import beam.agentsim.agents.modalbehaviors.ChoosesMode._ import beam.agentsim.agents.modalbehaviors.DrivesVehicle.{ActualVehicle, Token, VehicleOrToken} +import beam.agentsim.agents.planning.Strategy.ModeChoiceStrategy import beam.agentsim.agents.ridehail.{RideHailInquiry, RideHailRequest, RideHailResponse} import beam.agentsim.agents.vehicles.AccessErrorCodes.RideHailNotRequestedError import beam.agentsim.agents.vehicles.VehicleCategory.VehicleCategory @@ -132,8 +133,10 @@ trait ChoosesMode { val choosesModeData: ChoosesModeData = nextStateData.asInstanceOf[ChoosesModeData] val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) val nextAct = nextActivity(choosesModeData.personData).get + val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( choosesModeData.personData.currentTripMode, + currentTourMode, choosesModeData.personData, nextAct, availableModes @@ -143,8 +146,8 @@ trait ChoosesMode { case data: ChoosesModeData if data.personData.currentTourPersonalVehicle.isDefined && ( - data.personData.currentTourMode.exists(mode => mode == CAR || mode == BIKE) || - data.personData.currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) + currentTourMode.exists(mode => mode == CAR || mode == BIKE) || + currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) && isLastTripWithinTour(data.personData, nextAct) ) => self ! MobilityStatusResponse( @@ -227,9 +230,15 @@ trait ChoosesMode { val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) val personData = choosesModeData.personData val nextAct = nextActivity(personData).get + val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) // Make sure the current mode is allowable - val correctedCurrentTripMode = - correctCurrentTripModeAccordingToRules(personData.currentTripMode, personData, nextAct, availableModes) + val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( + personData.currentTripMode, + currentTourMode, + personData, + nextAct, + availableModes + ) val bodyStreetVehicle = createBodyStreetVehicle(currentPersonLocation) val departTime = _currentTick.get @@ -697,17 +706,18 @@ trait ChoosesMode { private def correctCurrentTripModeAccordingToRules( currentTripMode: Option[BeamMode], + currentTourMode: Option[BeamMode], personData: BasePersonData, nextAct: Activity, availableModes: Seq[BeamMode] ): Option[BeamMode] = { val replanningIsAvailable = personData.numberOfReplanningAttempts < beamServices.beamConfig.beam.agentsim.agents.modalBehaviors.maximumNumberOfReplanningAttempts - (currentTripMode, personData.currentTourMode) match { - case (_, Some(CAR | BIKE)) if personData.currentTourPersonalVehicle.isDefined => personData.currentTourMode + (currentTripMode, currentTourMode) match { + case (_, Some(CAR | BIKE)) if personData.currentTourPersonalVehicle.isDefined => currentTourMode case (_, Some(DRIVE_TRANSIT | BIKE_TRANSIT)) if personData.currentTourPersonalVehicle.isDefined && isLastTripWithinTour(personData, nextAct) => - personData.currentTourMode + currentTourMode case (Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)), _) if availableModes.contains(CAR) && replanningIsAvailable => Some(mode) @@ -1237,8 +1247,15 @@ trait ChoosesMode { gotoFinishingModeChoice(bushwhackingTrip) } case Some(_) => + val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) val correctedTripMode = - correctCurrentTripModeAccordingToRules(None, personData, nextAct, availableModesForTrips) + correctCurrentTripModeAccordingToRules( + None, + currentTourMode, + personData, + nextAct, + availableModesForTrips + ) if (correctedTripMode != personData.currentTripMode) { //give another chance to make a choice without predefined mode gotoChoosingModeWithoutPredefinedMode(choosesModeData) @@ -1397,6 +1414,10 @@ trait ChoosesMode { _experiencedBeamPlan.activities(data.personData.currentActivityIndex).getAttributes.getAttribute("trip_id") ).getOrElse("").toString + val nextAct = nextActivity(data.personData).get + val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) + val currentTrip = _experiencedBeamPlan.getTripContaining(nextAct) + val modeChoiceEvent = new ModeChoiceEvent( tick, id, @@ -1407,10 +1428,10 @@ trait ChoosesMode { data.availableAlternatives.get, data.availablePersonalStreetVehicles.nonEmpty, chosenTrip.legs.view.map(_.beamLeg.travelPath.distanceInM).sum, - _experiencedBeamPlan.tourIndexOfElement(nextActivity(data.personData).get), + _experiencedBeamPlan.tourIndexOfElement(nextAct), chosenTrip, _experiencedBeamPlan.activities(data.personData.currentActivityIndex).getType, - nextActivity(data.personData).get.getType, + nextAct.getType, tripId ) eventsManager.processEvent(modeChoiceEvent) @@ -1427,9 +1448,11 @@ trait ChoosesMode { ) ) + val strategy = ModeChoiceStrategy(data.personData.currentTripMode) + _experiencedBeamPlan.putStrategy(currentTrip, strategy) + _experiencedBeamPlan.putStrategy(currentTour, strategy) goto(Teleporting) using data.personData.copy( currentTrip = Some(chosenTrip), - currentTourMode = data.personData.currentTripMode, restOfCurrentTrip = List() ) @@ -1460,11 +1483,15 @@ trait ChoosesMode { ) ) ) + val chosenTripMode = Some(chosenTrip.tripClassifier) + val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) + val currentTourModeToStore = currentTourMode.orElse(chosenTripMode) + _experiencedBeamPlan.putStrategy(currentTrip, ModeChoiceStrategy(chosenTripMode)) + _experiencedBeamPlan.putStrategy(currentTour, ModeChoiceStrategy(currentTourModeToStore)) goto(WaitingForDeparture) using data.personData.copy( currentTrip = Some(chosenTrip), restOfCurrentTrip = chosenTrip.legs.toList, - currentTourMode = data.personData.currentTourMode.orElse(Some(chosenTrip.tripClassifier)), - currentTripMode = data.personData.currentTripMode.orElse(Some(chosenTrip.tripClassifier)), + currentTripMode = data.personData.currentTripMode.orElse(chosenTripMode), currentTourPersonalVehicle = if (isCurrentPersonalVehicleVoided) vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 26fb0da164b..ccb3719cd2e 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -1,7 +1,8 @@ package beam.agentsim.agents.planning -import java.{lang, util} +import beam.agentsim.agents.planning.BeamPlan.atHome +import java.{lang, util} import beam.agentsim.agents.planning.Strategy.{ModeChoiceStrategy, Strategy} import beam.router.Modes.BeamMode import org.matsim.api.core.v01.population._ @@ -10,6 +11,7 @@ import org.matsim.utils.objectattributes.attributable.Attributes import scala.collection.JavaConverters._ import scala.collection.mutable +import scala.reflect.ClassTag /** * BeamPlan @@ -85,6 +87,8 @@ object BeamPlan { newPlan.setScore(plan.getScore) newPlan } + + def atHome(activity: Activity): Boolean = activity.getType.equalsIgnoreCase("home") } class BeamPlan extends Plan { @@ -115,7 +119,7 @@ class BeamPlan extends Plan { case activity: Activity => val nextTrip = Trip(activity, nextLeg, nextTour) nextTour.addTrip(nextTrip) - if (activity.getType.equalsIgnoreCase("home")) { + if (atHome(activity)) { tours = tours :+ nextTour nextTour = new Tour } @@ -145,14 +149,14 @@ class BeamPlan extends Plan { } def putStrategy(planElement: PlanElement, strategy: Strategy): Unit = { - if (!strategies.contains(planElement)) { - strategies.put(planElement, mutable.Map[Class[_ <: Strategy], Strategy]()) - } - strategies(planElement).put(strategy.getClass, strategy) + val planElementMap = strategies.getOrElseUpdate(planElement, mutable.Map.empty[Class[_ <: Strategy], Strategy]) + planElementMap.put(strategy.getClass, strategy) planElement match { case tour: Tour => - tour.trips.foreach(trip => putStrategy(trip, strategy)) + strategy.tripStrategies(tour).foreach { case (trip, strategy) => + putStrategy(trip, strategy) + } case trip: Trip => putStrategy(trip.activity, strategy) trip.leg.foreach(theLeg => putStrategy(theLeg, strategy)) @@ -161,8 +165,17 @@ class BeamPlan extends Plan { } } - def getStrategy(planElement: PlanElement, forClass: Class[_ <: Strategy]): Option[Strategy] = { - strategies.getOrElse(planElement, mutable.Map()).get(forClass) + def getStrategy[T <: Strategy: ClassTag](planElement: PlanElement): Option[T] = { + val forClass: Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]] + strategies.getOrElse(planElement, Map.empty[Class[_ <: Strategy], Strategy]).get(forClass).asInstanceOf[Option[T]] + } + + def getTripStrategy[T <: Strategy: ClassTag](activity: Activity): Option[T] = { + getStrategy(actsLegToTrip(activity)) + } + + def getTourStrategy[T <: Strategy: ClassTag](activity: Activity): Option[T] = { + getStrategy(getTourContaining(activity)) } def isLastElementInTour(planElement: PlanElement): Boolean = { @@ -204,6 +217,14 @@ class BeamPlan extends Plan { } } + def getTourContaining(index: Int): Tour = { + getTourContaining(activities(index)) + } + + def getTripContaining(index: Int): Trip = { + getTripContaining(activities(index)) + } + ////////////////////////////////////////////////////////////////////// // Supporting legacy interface ////////////////////////////////////////////////////////////////////// diff --git a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala index a96d7a93e48..561e72166ec 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala @@ -7,7 +7,9 @@ import beam.router.Modes.BeamMode */ object Strategy { - sealed trait Strategy + trait Strategy { + def tripStrategies(tour: Tour): Seq[(Trip, Strategy)] = Seq.empty + } case class ModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy diff --git a/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala b/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala index f72479fafe4..b3866df31e0 100755 --- a/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala +++ b/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala @@ -52,47 +52,49 @@ class BeamPlanSpec extends AnyWordSpecLike with Matchers with BeamHelper { val beamPlan = BeamPlan(matsimPlan) val act = beamPlan.activities.head beamPlan.putStrategy(act, strat) - beamPlan.getStrategy(act, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](act) should be(Some(strat)) } "should attach a strategy to a leg" in { val beamPlan = BeamPlan(matsimPlan) val leg = beamPlan.legs.head beamPlan.putStrategy(leg, strat) - beamPlan.getStrategy(leg, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) } "should attach a strategy to a trip" in { val beamPlan = BeamPlan(matsimPlan) val trip = beamPlan.trips.head beamPlan.putStrategy(trip, strat) - beamPlan.getStrategy(trip, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](trip) should be(Some(strat)) } "should attach a strategy to a tour" in { val beamPlan = BeamPlan(matsimPlan) val tour = beamPlan.tours.head beamPlan.putStrategy(tour, strat) - beamPlan.getStrategy(tour, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](tour) should be(Some(strat)) } "should attach a strategy to a trip and the trip's activity and leg" in { val beamPlan = BeamPlan(matsimPlan) val trip = beamPlan.trips.head beamPlan.putStrategy(trip, strat) - beamPlan.getStrategy(trip.activity, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](trip.activity) should be(Some(strat)) trip.leg match { case Some(leg) => - beamPlan.getStrategy(leg, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) case None => } } - "should attach a strategy to a tour and the tour's trips, activities, and trips" in { + "should not attach a strategy to tour's trips, activities, and legs" in { val beamPlan = BeamPlan(matsimPlan) - val tour = beamPlan.tours.head - beamPlan.putStrategy(tour, strat) + val tour = beamPlan.tours(1) + val strategy = ModeChoiceStrategy(None) + beamPlan.putStrategy(tour, strategy) + beamPlan.getStrategy[ModeChoiceStrategy](tour) should be(Some(strategy)) tour.trips.foreach { trip => - beamPlan.getStrategy(trip, classOf[ModeChoiceStrategy]) should be(Some(strat)) - beamPlan.getStrategy(trip.activity, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](trip) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](trip.activity) should be(Some(strat)) trip.leg match { case Some(leg) => - beamPlan.getStrategy(leg, classOf[ModeChoiceStrategy]) should be(Some(strat)) + beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) case None => } } diff --git a/src/test/scala/beam/integration/SingleModeSpec.scala b/src/test/scala/beam/integration/SingleModeSpec.scala index 0764c304fbe..43e4faafad9 100755 --- a/src/test/scala/beam/integration/SingleModeSpec.scala +++ b/src/test/scala/beam/integration/SingleModeSpec.scala @@ -154,8 +154,10 @@ class SingleModeSpec val newPlanElements = person.getSelectedPlan.getPlanElements.asScala.collect { case activity: Activity if activity.getType == "Home" => Seq(activity, scenario.getPopulation.getFactory.createLeg("drive_transit")) - case activity: Activity => Seq(activity) - case _: Leg => Nil + case activity: Activity => + Seq(activity) + Seq(activity, scenario.getPopulation.getFactory.createLeg("")) + case _: Leg => Nil }.flatten if (newPlanElements.last.isInstanceOf[Leg]) { newPlanElements.remove(newPlanElements.size - 1) @@ -205,7 +207,9 @@ class SingleModeSpec val regularPersonEvents = filterOutProfessionalDriversAndCavs(personDepartureEvents) val (driveTransit, others) = regularPersonEvents.map(_.getLegMode).partition(_ == "drive_transit") //router gives too little 'drive transit' trips, most of the persons chooses 'car' in this case - others.count(_ == "walk_transit") should be < (0.2 * driveTransit.size).toInt + withClue("When transit is available majority of agents should use drive_transit") { + others.count(_ == "walk_transit") should be < MathUtils.doubleToInt(0.2 * driveTransit.size) + } val eventsByPerson = events.groupBy(_.getAttributes.get("person")) From eb4fafbd11144b6d48edb4d82f600e028439247c Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Mon, 28 Mar 2022 18:50:58 +0300 Subject: [PATCH 09/40] Removed unused parameter from an example in inputs.rst --- docs/inputs.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/inputs.rst b/docs/inputs.rst index bff0f8f1ce5..da66699bfa7 100755 --- a/docs/inputs.rst +++ b/docs/inputs.rst @@ -236,7 +236,6 @@ beam.routing { # Departure window in min departureWindow = "double | 15.0" numberOfSamples = "int | 1" - osmFile = ${beam.routing.r5.directory}"/beamville.osm.pbf" osmMapdbFile = ${beam.routing.r5.directory}"/osm.mapdb" mNetBuilder.fromCRS = "EPSG:4326" # WGS84 mNetBuilder.toCRS = "EPSG:26910" # UTM10N From 103a0c3e3e2ab6cceb36114c44f08ffd09610d9c Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Tue, 29 Mar 2022 17:22:49 +0300 Subject: [PATCH 10/40] Moved trip mode calculation to ModeChoiceStrategy --- .../agents/modalbehaviors/ChoosesMode.scala | 36 ++++------------- .../agentsim/agents/planning/BeamPlan.scala | 2 +- .../agentsim/agents/planning/Strategy.scala | 40 ++++++++++++++++++- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 5e10e467afc..4449bf96abc 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -136,9 +136,7 @@ trait ChoosesMode { val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( choosesModeData.personData.currentTripMode, - currentTourMode, choosesModeData.personData, - nextAct, availableModes ) nextStateData match { @@ -230,13 +228,10 @@ trait ChoosesMode { val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) val personData = choosesModeData.personData val nextAct = nextActivity(personData).get - val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) // Make sure the current mode is allowable val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( personData.currentTripMode, - currentTourMode, personData, - nextAct, availableModes ) @@ -706,28 +701,19 @@ trait ChoosesMode { private def correctCurrentTripModeAccordingToRules( currentTripMode: Option[BeamMode], - currentTourMode: Option[BeamMode], personData: BasePersonData, - nextAct: Activity, availableModes: Seq[BeamMode] ): Option[BeamMode] = { val replanningIsAvailable = personData.numberOfReplanningAttempts < beamServices.beamConfig.beam.agentsim.agents.modalBehaviors.maximumNumberOfReplanningAttempts - (currentTripMode, currentTourMode) match { - case (_, Some(CAR | BIKE)) if personData.currentTourPersonalVehicle.isDefined => currentTourMode - case (_, Some(DRIVE_TRANSIT | BIKE_TRANSIT)) - if personData.currentTourPersonalVehicle.isDefined && isLastTripWithinTour(personData, nextAct) => - currentTourMode - case (Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)), _) + currentTripMode match { + case Some(mode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION)) if availableModes.contains(CAR) && replanningIsAvailable => Some(mode) - case (Some(CAR_HOV2 | CAR_HOV3), Some(tourMode @ (HOV2_TELEPORTATION | HOV3_TELEPORTATION))) - if availableModes.contains(CAR) && replanningIsAvailable => - Some(tourMode) - case (Some(mode), _) if availableModes.contains(mode) && replanningIsAvailable => Some(mode) - case (Some(mode), _) if availableModes.contains(mode) => Some(WALK) - case (None, _) if !replanningIsAvailable => Some(WALK) - case _ => None + case Some(mode) if availableModes.contains(mode) && replanningIsAvailable => Some(mode) + case Some(mode) if availableModes.contains(mode) => Some(WALK) + case None if !replanningIsAvailable => Some(WALK) + case _ => None } } @@ -1247,15 +1233,7 @@ trait ChoosesMode { gotoFinishingModeChoice(bushwhackingTrip) } case Some(_) => - val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) - val correctedTripMode = - correctCurrentTripModeAccordingToRules( - None, - currentTourMode, - personData, - nextAct, - availableModesForTrips - ) + val correctedTripMode = correctCurrentTripModeAccordingToRules(None, personData, availableModesForTrips) if (correctedTripMode != personData.currentTripMode) { //give another chance to make a choice without predefined mode gotoChoosingModeWithoutPredefinedMode(choosesModeData) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index ccb3719cd2e..6be4d59b132 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -154,7 +154,7 @@ class BeamPlan extends Plan { planElement match { case tour: Tour => - strategy.tripStrategies(tour).foreach { case (trip, strategy) => + strategy.tripStrategies(tour, this).foreach { case (trip, strategy) => putStrategy(trip, strategy) } case trip: Trip => diff --git a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala index 561e72166ec..db1f589fcde 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala @@ -1,6 +1,7 @@ package beam.agentsim.agents.planning import beam.router.Modes.BeamMode +import beam.router.Modes.BeamMode._ /** * BEAM @@ -8,9 +9,44 @@ import beam.router.Modes.BeamMode object Strategy { trait Strategy { - def tripStrategies(tour: Tour): Seq[(Trip, Strategy)] = Seq.empty + + /** + * When a tour strategy is set this method of that strategy is called. It allows to set strategies for the trips + * that this tour consists of + * @param tour the tour that this strategy is set for + * @param beamPlan the whole beam plan + * @return trips with strategies that needs to be set + */ + def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = Seq.empty } - case class ModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy + case class ModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy { + + override def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = { + val tourStrategy = this + + mode match { + case Some(CAR | BIKE) => + tour.trips.zip(Seq.fill(tour.trips.size)(tourStrategy)) + case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => + //replace both ends with DRIVE_TRANSIT or BIKE_TRANSIT + val firstTrip = tour.trips.head + val lastTrip = tour.trips.last + Seq(firstTrip -> tourStrategy, lastTrip -> tourStrategy) + case Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION) => + //we need to replace all CAR_HOV modes to this tour mode + //because these CAR_HOV modes shouldn't be used in this type of tour + tour.trips + .withFilter { trip => + beamPlan.getStrategy[ModeChoiceStrategy](trip).flatMap(_.mode) match { + case Some(CAR_HOV2 | CAR_HOV3) => true + case _ => false + } + } + .map(_ -> tourStrategy) + case _ => super.tripStrategies(tour, beamPlan) + } + } + } } From c05b00f2eead8f2f99439eb0892183334a55d659 Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Tue, 5 Apr 2022 16:48:30 +0300 Subject: [PATCH 11/40] Save current trip/tour mode to ModeChoiceEvent. Finding correct tour strategy basing on current trip strategy. --- .../agents/modalbehaviors/ChoosesMode.scala | 18 +++++++----------- .../agentsim/agents/planning/Strategy.scala | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 4449bf96abc..325ef3bad41 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -1395,12 +1395,16 @@ trait ChoosesMode { val nextAct = nextActivity(data.personData).get val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) val currentTrip = _experiencedBeamPlan.getTripContaining(nextAct) + val tripStrategy = ModeChoiceStrategy(Some(chosenTrip.tripClassifier)) + val currentTourStrategy = tripStrategy.tourStrategy(_experiencedBeamPlan, currentActivity(data.personData), nextAct) + _experiencedBeamPlan.putStrategy(currentTrip, tripStrategy) + _experiencedBeamPlan.putStrategy(currentTour, currentTourStrategy) val modeChoiceEvent = new ModeChoiceEvent( tick, id, chosenTrip.tripClassifier.value, - data.personData.currentTripMode.map(_.value).getOrElse(""), + currentTourStrategy.mode.map(_.value).getOrElse(""), data.expectedMaxUtilityOfLatestChoice.getOrElse[Double](Double.NaN), _experiencedBeamPlan.activities(data.personData.currentActivityIndex).getLinkId.toString, data.availableAlternatives.get, @@ -1415,7 +1419,7 @@ trait ChoosesMode { eventsManager.processEvent(modeChoiceEvent) data.personData.currentTripMode match { - case Some(HOV2_TELEPORTATION | HOV3_TELEPORTATION) => + case Some(mode) if mode.isHovTeleportation => scheduler ! CompletionNotice( triggerId, Vector( @@ -1426,9 +1430,6 @@ trait ChoosesMode { ) ) - val strategy = ModeChoiceStrategy(data.personData.currentTripMode) - _experiencedBeamPlan.putStrategy(currentTrip, strategy) - _experiencedBeamPlan.putStrategy(currentTour, strategy) goto(Teleporting) using data.personData.copy( currentTrip = Some(chosenTrip), restOfCurrentTrip = List() @@ -1461,15 +1462,10 @@ trait ChoosesMode { ) ) ) - val chosenTripMode = Some(chosenTrip.tripClassifier) - val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) - val currentTourModeToStore = currentTourMode.orElse(chosenTripMode) - _experiencedBeamPlan.putStrategy(currentTrip, ModeChoiceStrategy(chosenTripMode)) - _experiencedBeamPlan.putStrategy(currentTour, ModeChoiceStrategy(currentTourModeToStore)) goto(WaitingForDeparture) using data.personData.copy( currentTrip = Some(chosenTrip), restOfCurrentTrip = chosenTrip.legs.toList, - currentTripMode = data.personData.currentTripMode.orElse(chosenTripMode), + currentTripMode = Some(chosenTrip.tripClassifier), currentTourPersonalVehicle = if (isCurrentPersonalVehicleVoided) vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id) diff --git a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala index db1f589fcde..05facbf6872 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala @@ -1,7 +1,9 @@ package beam.agentsim.agents.planning +import beam.agentsim.agents.planning.BeamPlan.atHome import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode._ +import org.matsim.api.core.v01.population.Activity /** * BEAM @@ -47,6 +49,19 @@ object Strategy { case _ => super.tripStrategies(tour, beamPlan) } } + + def tourStrategy(beamPlan: BeamPlan, curAct: Activity, nextAct: Activity): ModeChoiceStrategy = { + val currentTourModeOpt = beamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) + val newTourMode = currentTourModeOpt match { + case Some(_) if mode.get.isHovTeleportation => mode + case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => currentTourModeOpt + case _ if atHome(curAct) => mode + case Some(_) => currentTourModeOpt + case None => mode + } + ModeChoiceStrategy(newTourMode) + } + } } From 2059ebf90d3072258b5cfd993bdaa63c758af11b Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Thu, 7 Apr 2022 12:33:18 +0300 Subject: [PATCH 12/40] Warehouse activity name changed to Home in order to get Beam works correctly with tours --- src/main/R/freight/freight-processing.R | 2 +- .../beam/agentsim/agents/freight/input/FreightReader.scala | 4 ++-- .../beam/agentsim/agents/freight/FreightReplannerSpec.scala | 4 ++-- .../agents/freight/input/GenericFreightReaderSpec.scala | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/R/freight/freight-processing.R b/src/main/R/freight/freight-processing.R index 03e5a2c885e..4e83f661795 100644 --- a/src/main/R/freight/freight-processing.R +++ b/src/main/R/freight/freight-processing.R @@ -17,7 +17,7 @@ events <- readCsv(pp(workDir, "/5.events.csv.gz")) unloading <- events[actType=="Unloading"] nrow(unloading[type=="actstart"]) nrow(unloading[type=="actend"]) -warehouse <- events[actType=="Warehouse"] +warehouse <- events[actType=="Home"] nrow(warehouse[type=="actstart"]) nrow(warehouse[type=="actend"]) pt <- events[type=="PathTraversal"][,c("time","type","vehicleType","vehicle","secondaryFuelLevel", diff --git a/src/main/scala/beam/agentsim/agents/freight/input/FreightReader.scala b/src/main/scala/beam/agentsim/agents/freight/input/FreightReader.scala index 9a88c610ff9..2e40615c72c 100644 --- a/src/main/scala/beam/agentsim/agents/freight/input/FreightReader.scala +++ b/src/main/scala/beam/agentsim/agents/freight/input/FreightReader.scala @@ -55,7 +55,7 @@ trait FreightReader { ): Plan = { val allToursPlanElements = tours.flatMap { tour => val tourInitialActivity = - createFreightActivity("Warehouse", tour.warehouseLocationUTM, tour.departureTimeInSec, None) + createFreightActivity("Home", tour.warehouseLocationUTM, tour.departureTimeInSec, None) val firstLeg: Leg = createFreightLeg(tour.departureTimeInSec) val plans: IndexedSeq[PayloadPlan] = plansPerTour.get(tour.tourId) match { @@ -82,7 +82,7 @@ trait FreightReader { elements } - val finalActivity = createFreightActivity("Warehouse", tours.head.warehouseLocationUTM, -1, None) + val finalActivity = createFreightActivity("Home", tours.head.warehouseLocationUTM, -1, None) val allPlanElements: IndexedSeq[PlanElement] = allToursPlanElements :+ finalActivity val currentPlan = PopulationUtils.createPlan(person) diff --git a/src/test/scala/beam/agentsim/agents/freight/FreightReplannerSpec.scala b/src/test/scala/beam/agentsim/agents/freight/FreightReplannerSpec.scala index b68fa905a13..ff18111ad48 100644 --- a/src/test/scala/beam/agentsim/agents/freight/FreightReplannerSpec.scala +++ b/src/test/scala/beam/agentsim/agents/freight/FreightReplannerSpec.scala @@ -127,12 +127,12 @@ class FreightReplannerSpec extends AnyWordSpecLike with Matchers with BeamHelper plan.getPlanElements should have size 9 plan.getPlanElements.get(0) shouldBe a[Activity] val activity0 = plan.getPlanElements.get(0).asInstanceOf[Activity] - activity0.getType should be("Warehouse") + activity0.getType should be("Home") activity0.getEndTime should be(11915.0) plan.getPlanElements.get(2).asInstanceOf[Activity].getType should be("Unloading") plan.getPlanElements.get(4).asInstanceOf[Activity].getType should be("Unloading") plan.getPlanElements.get(6).asInstanceOf[Activity].getType should be("Loading") - plan.getPlanElements.get(8).asInstanceOf[Activity].getType should be("Warehouse") + plan.getPlanElements.get(8).asInstanceOf[Activity].getType should be("Home") } } diff --git a/src/test/scala/beam/agentsim/agents/freight/input/GenericFreightReaderSpec.scala b/src/test/scala/beam/agentsim/agents/freight/input/GenericFreightReaderSpec.scala index 12042d72cb8..a093b00b2c7 100644 --- a/src/test/scala/beam/agentsim/agents/freight/input/GenericFreightReaderSpec.scala +++ b/src/test/scala/beam/agentsim/agents/freight/input/GenericFreightReaderSpec.scala @@ -149,7 +149,7 @@ class GenericFreightReaderSpec extends AnyWordSpecLike with Matchers { plan4.getPlanElements.get(2).asInstanceOf[Activity].getCoord should be( new Coord(169900.11498160253, 3510.2356380579545) ) - plan4.getPlanElements.get(4).asInstanceOf[Activity].getType should be("Warehouse") + plan4.getPlanElements.get(4).asInstanceOf[Activity].getType should be("Home") } } From 12f5346b133d70dd90333ebc96602efcc6f5056d Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Thu, 7 Apr 2022 12:33:48 +0300 Subject: [PATCH 13/40] Fixed freight --- .../scala/beam/agentsim/agents/household/HouseholdActor.scala | 2 +- test/input/sf-light/freight/freight-tours.csv | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala index b8fdf8dc9b0..a8aebf4c673 100755 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala @@ -210,7 +210,7 @@ object HouseholdActor { //We should create a vehicle manager for cars and bikes for all households in case they are generated during the simulation - val vehiclesByAllCategories = List(Car, Bike) + val vehiclesByAllCategories = (List(Car, Bike) ++ vehiclesByCategory.keys).distinct .map(cat => cat -> vehiclesByCategory.getOrElse(cat, Map[Id[BeamVehicle], BeamVehicle]())) .toMap val fleetManagers = vehiclesByAllCategories.map { case (category, vehicleMap) => diff --git a/test/input/sf-light/freight/freight-tours.csv b/test/input/sf-light/freight/freight-tours.csv index 8a7cf95ccc6..af9f07c0269 100644 --- a/test/input/sf-light/freight/freight-tours.csv +++ b/test/input/sf-light/freight/freight-tours.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6c6a974285e0154ecbb05246c0999c22b85cac9c068c03717e403c6b64a006b -size 153 +oid sha256:e7be0b20871409f3e90a10e445e374831a4a63366a4a16910cb2739c3677e4dc +size 161 From 4d3e22d26e06e2bb8298798671cbeb177a210ee8 Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Thu, 14 Apr 2022 18:46:46 +0300 Subject: [PATCH 14/40] Fixed availableVehicles disappearing when the person is not at home. --- .../agentsim/agents/household/HouseholdFleetManager.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala index 1f39666be49..bf92ad135e1 100644 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdFleetManager.scala @@ -152,7 +152,7 @@ class HouseholdFleetManager( rest case _ => logger.error(s"THE LIST OF VEHICLES SHOULDN'T BE EMPTY") - Nil + availableVehicles } } } @@ -168,7 +168,7 @@ class HouseholdFleetManager( case _ => logger.debug(s"Not returning vehicle because no default is defined or agent is not at home") sender() ! MobilityStatusResponse(Vector(), triggerId) - Nil + availableVehicles } } From 0d8510de19da62a2c520a243ff13032812fdf497 Mon Sep 17 00:00:00 2001 From: Dmitry Openkov Date: Tue, 19 Apr 2022 10:39:58 +0300 Subject: [PATCH 15/40] Set chargingType column to chargingPointType to fix parking.csv --- test/input/sf-light/freight/freight-depots.csv.gz | 4 ++-- test/input/sf-light/taz-parking.csv | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/input/sf-light/freight/freight-depots.csv.gz b/test/input/sf-light/freight/freight-depots.csv.gz index 814699157f6..129ba1951aa 100644 --- a/test/input/sf-light/freight/freight-depots.csv.gz +++ b/test/input/sf-light/freight/freight-depots.csv.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fcd7271e8bae1d84c0c591ef9aa4dbba403284d47e0e11aa6eca910fd7fc980 -size 30826 +oid sha256:d6c455cf72f372ec73b09778eae08a7c8aaf40a38fdacf37010e7f8a6b5bdef4 +size 35777 diff --git a/test/input/sf-light/taz-parking.csv b/test/input/sf-light/taz-parking.csv index a5dfbaa4832..e8e022501d9 100644 --- a/test/input/sf-light/taz-parking.csv +++ b/test/input/sf-light/taz-parking.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec529a2d978a87a4117a19b4f8cada0f9c521faa21c3578a17d91aa4298a1b28 -size 1636492 +oid sha256:093a26c285c5bd9ca090c185697778e3f4ac663eae6ca33e5188f21157372e24 +size 1636497 From fc221f551baf6c06eea07bb347cfe3ba81f4a473 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Wed, 1 Jun 2022 17:29:02 -0700 Subject: [PATCH 16/40] tour mode choice, initial commit --- .../beam/agentsim/agents/PersonAgent.scala | 44 ++++++++------ .../choice/logit/TourModeChoiceModel.scala | 32 ++++++++++ .../mode/TourModeChoiceMultinomialLogit.scala | 56 ++++++++++++++++++ .../agents/modalbehaviors/ChoosesMode.scala | 31 ++++++---- .../agentsim/agents/planning/BeamPlan.scala | 50 +++++++++++----- .../agentsim/agents/planning/Strategy.scala | 16 +++-- .../beam/agentsim/agents/planning/Tour.scala | 7 ++- src/main/scala/beam/router/Modes.scala | 59 +++++++++++++++++-- .../beam/router/skim/core/ODSkimmer.scala | 19 ++++++ .../beam/router/skim/readonly/ODSkims.scala | 33 ++++++++++- .../beam/agentsim/planning/BeamPlanSpec.scala | 26 ++++---- 11 files changed, 301 insertions(+), 72 deletions(-) create mode 100644 src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala create mode 100644 src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 3c612bc427b..8b8fcca7862 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -18,7 +18,7 @@ import beam.agentsim.agents.parking.ChoosesParking.{ ReleasingParkingSpot } import beam.agentsim.agents.planning.BeamPlan.atHome -import beam.agentsim.agents.planning.Strategy.ModeChoiceStrategy +import beam.agentsim.agents.planning.Strategy.{TourModeChoiceStrategy, TripModeChoiceStrategy} import beam.agentsim.agents.planning.{BeamPlan, Tour} import beam.agentsim.agents.ridehail.RideHailManager.TravelProposal import beam.agentsim.agents.ridehail._ @@ -36,6 +36,7 @@ import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, IllegalTrig import beam.agentsim.scheduler.Trigger.TriggerWithId import beam.agentsim.scheduler.{BeamAgentSchedulerTimer, Trigger} import beam.router.Modes.BeamMode +import beam.router.TourModes.BeamTourMode import beam.router.Modes.BeamMode.{ CAR, CAV, @@ -190,8 +191,9 @@ object PersonAgent { currentTrip: Option[EmbodiedBeamTrip] = None, restOfCurrentTrip: List[EmbodiedBeamLeg] = List(), currentVehicle: VehicleStack = Vector(), - currentTripMode: Option[BeamMode] = None, - currentTourPersonalVehicle: Option[Id[BeamVehicle]] = None, + currentTripMode: Option[BeamMode] = None, // We might not need this here any more if it's kept in the plan + currentTourMode: Option[BeamTourMode] = None, // "" + currentTourPersonalVehicle: Option[Id[BeamVehicle]] = None, // "" passengerSchedule: PassengerSchedule = PassengerSchedule(), currentLegPassengerScheduleIndex: Int = 0, hasDeparted: Boolean = false, @@ -603,7 +605,8 @@ class PersonAgent( case Some(nextAct) => logDebug(s"wants to go to ${nextAct.getType} @ $tick") holdTickAndTriggerId(tick, triggerId) - val modeOfNextLeg = _experiencedBeamPlan.getTripStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) + val modeOfNextLeg = _experiencedBeamPlan.getTripStrategy[TripModeChoiceStrategy](nextAct).mode + val currentTourModeChoiceStrategy = _experiencedBeamPlan.getTourStrategy[TourModeChoiceStrategy](nextAct) val currentCoord = currentActivity(data).getCoord val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( @@ -612,6 +615,7 @@ class PersonAgent( // If we have the currentTourPersonalVehicle then we should use it // use the mode of the next leg as the new trip mode. currentTripMode = modeOfNextLeg, + currentTourMode = currentTourModeChoiceStrategy.tourMode, numberOfReplanningAttempts = 0, failedTrips = IndexedSeq.empty, enrouteData = EnrouteData() @@ -628,7 +632,7 @@ class PersonAgent( when(Teleporting) { case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -642,7 +646,7 @@ class PersonAgent( case Event( TriggerWithId(TeleportationEndsTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, maybeCurrentTripMode, _, _, _, true, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, maybeCurrentTripMode, _, _, _, _, true, _, _, _, _, _) ) => holdTickAndTriggerId(tick, triggerId) @@ -674,7 +678,7 @@ class PersonAgent( */ case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, false, _, _, _, _, _) + data @ BasePersonData(_, Some(currentTrip), _, _, _, _, _, _, _, false, _, _, _, _, _) ) => endActivityAndDepart(tick, currentTrip, data) @@ -683,7 +687,7 @@ class PersonAgent( case Event( TriggerWithId(PersonDepartureTrigger(tick), triggerId), - BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, true, _, _, _, _, _) + BasePersonData(_, _, restOfCurrentTrip, _, _, _, _, _, _, true, _, _, _, _, _) ) => // We're coming back from replanning, i.e. we are already on the trip, so we don't throw a departure event logDebug(s"replanned to leg ${restOfCurrentTrip.head}") @@ -758,7 +762,7 @@ class PersonAgent( // TRANSIT FAILURE case Event( ReservationResponse(Left(firstErrorResponse), _), - data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"replanning because ${firstErrorResponse.errorCode}") @@ -843,7 +847,7 @@ class PersonAgent( */ case Event( TriggerWithId(BoardVehicleTrigger(tick, vehicleToEnter), triggerId), - data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, currentLeg :: _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) => logDebug(s"PersonEntersVehicle: $vehicleToEnter @ $tick") eventsManager.processEvent(new PersonEntersVehicleEvent(tick, id, vehicleToEnter)) @@ -876,7 +880,7 @@ class PersonAgent( */ case Event( TriggerWithId(AlightVehicleTrigger(tick, vehicleToExit, energyConsumedOption), triggerId), - data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _) + data @ BasePersonData(_, _, _ :: restOfCurrentTrip, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) if vehicleToExit.equals(currentVehicle.head) => updateFuelConsumed(energyConsumedOption) logDebug(s"PersonLeavesVehicle: $vehicleToExit @ $tick") @@ -953,6 +957,7 @@ class PersonAgent( _, _, _, + _, currentCost, _, _, @@ -995,7 +1000,7 @@ class PersonAgent( beamServices.geo.wgs2Utm(basePersonData.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc val nextCoord = nextActivity(basePersonData).get.getCoord // Have to give up my mode as well, perhaps there's no option left for driving. - _experiencedBeamPlan.putStrategy(currentTour(basePersonData), ModeChoiceStrategy(mode = None)) + _experiencedBeamPlan.putStrategy(currentTour(basePersonData), TripModeChoiceStrategy(mode = None)) goto(ChoosingMode) using ChoosesModeData( basePersonData.copy( currentTripMode = None, @@ -1095,6 +1100,7 @@ class PersonAgent( _, _, _, + _, _ ) ) if nextLeg.asDriver => @@ -1189,7 +1195,7 @@ class PersonAgent( nextState // TRANSIT but too late - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit && nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the bus. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1212,7 +1218,7 @@ class PersonAgent( else Vector(BeamMode.RIDE_HAIL, BeamMode.CAR, BeamMode.CAV) ) // TRANSIT - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.mode.isTransit => val resRequest = TransitReservationRequest( nextLeg.beamLeg.travelPath.transitStops.get.fromIdx, @@ -1223,7 +1229,7 @@ class PersonAgent( TransitDriverAgent.selectByVehicleId(nextLeg.beamVehicleId) ! resRequest goto(WaitingForReservationConfirmation) // RIDE_HAIL - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.isRideHail => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) @@ -1251,7 +1257,7 @@ class PersonAgent( goto(WaitingForReservationConfirmation) // CAV but too late // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _)) + case Event(StateTimeout, data @ BasePersonData(_, _, nextLeg :: _, _, _, _, _, _, _, _, _, _, _, _, _)) if nextLeg.beamLeg.startTime < _currentTick.get => // We've missed the CAV. This occurs when something takes longer than planned (based on the // initial inquiry). So we replan but change tour mode to WALK_TRANSIT since we've already done our non-transit @@ -1275,7 +1281,7 @@ class PersonAgent( ) // CAV // TODO: Refactor so it uses literally the same code block as transit - case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _)) => + case Event(StateTimeout, BasePersonData(_, _, nextLeg :: tailOfCurrentTrip, _, _, _, _, _, _, _, _, _, _, _, _)) => val legSegment = nextLeg :: tailOfCurrentTrip.takeWhile(leg => leg.beamVehicleId == nextLeg.beamVehicleId) val resRequest = ReservationRequest( legSegment.head.beamLeg, @@ -1304,6 +1310,7 @@ class PersonAgent( _, _, _, + _, _ ) ) => @@ -1353,6 +1360,7 @@ class PersonAgent( _, _, _, + _, currentTourPersonalVehicle, _, _, @@ -1582,7 +1590,7 @@ class PersonAgent( handleBoardOrAlightOutOfPlace case Event( TriggerWithId(BoardVehicleTrigger(_, vehicleId), triggerId), - BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _) + BasePersonData(_, _, _, currentVehicle, _, _, _, _, _, _, _, _, _, _, _) ) if currentVehicle.nonEmpty && currentVehicle.head.equals(vehicleId) => log.debug("Person {} in state {} received Board for vehicle that he is already on, ignoring...", id, stateName) stay() replying CompletionNotice(triggerId, Vector()) diff --git a/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala b/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala new file mode 100644 index 00000000000..c4855041951 --- /dev/null +++ b/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala @@ -0,0 +1,32 @@ +package beam.agentsim.agents.choice.logit + +import beam.sim.config.BeamConfig +import beam.sim.population.AttributesOfIndividual +import org.matsim.api.core.v01.population.Activity + +import scala.collection.JavaConverters._ +import scala.util.{Failure, Success, Try} + +object TourModeChoiceModel { + def apply(beamConfig: BeamConfig) = new TourModeChoiceModel(beamConfig) + + sealed trait TourModeParameters + + object TourModeParameters { + final case object ExpectedMaxUtility extends TourModeParameters with Serializable + final case object Intercept extends TourModeParameters with Serializable + } + + type TourModeMNLConfig = Map[TourModeParameters, UtilityFunctionOperation] +} + +class TourModeChoiceModel( + val beamConfig: BeamConfig +) { + + val DefaultMNLParameters: TourModeChoiceModel.TourModeMNLConfig = Map( + TourModeChoiceModel.TourModeParameters.ExpectedMaxUtility -> UtilityFunctionOperation.Multiplier(1.0), + TourModeChoiceModel.TourModeParameters.Intercept -> UtilityFunctionOperation.Intercept(1.0) + ) + +} diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala new file mode 100644 index 00000000000..653e8640d9f --- /dev/null +++ b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala @@ -0,0 +1,56 @@ +package beam.agentsim.agents.choice.mode + +import beam.router.Modes.BeamMode +import beam.router.TourModes.BeamTourMode +import beam.router.TourModes.BeamTourMode.WALK_BASED +import beam.router.skim.core.ODSkimmer.ODSkimmerTimeCostTransfer +import beam.sim.config.BeamConfigHolder +import beam.sim.population.AttributesOfIndividual + +import scala.collection.mutable +import scala.util.Random + +class TourModeChoiceMultinomialLogit( + val attributesOfIndividual: AttributesOfIndividual, + val configHolder: BeamConfigHolder +) { + val rnd: Random = new scala.util.Random(System.currentTimeMillis()) + + val (_, modeLogit) = ModeChoiceMultinomialLogit.buildModelFromConfig(configHolder) + + def chooseTourMode( + tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]], + firstAndLastTripModeToTourMode: Option[Map[BeamTourMode, Seq[BeamMode]]] + ): BeamTourMode = { + + WALK_BASED + } + + def tripExpectedMaxUtility( + tripModeCosts: Map[BeamMode, ODSkimmerTimeCostTransfer], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] + ): Map[BeamTourMode, Double] = { + modeToTourMode map { case (beamTourMode, beamModes) => + val modeChoice = beamModes.map { beamMode => + val skims = tripModeCosts.getOrElse(beamMode, ODSkimmerTimeCostTransfer()) + val timeCost = attributesOfIndividual.getVOT(skims.timeInHours) + val monetaryCost = skims.cost + beamMode -> (Map("cost" -> (timeCost + monetaryCost)) ++ Map("transfers" -> skims.numTransfers)) + }.toMap + beamTourMode -> modeLogit.getExpectedMaximumUtility(modeChoice).getOrElse(Double.NegativeInfinity) + } + } + + def tourExpectedMaxUtility( + tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] + ): Map[BeamTourMode, Double] = { + val x = tourModeCosts.foldLeft(mutable.Map[BeamTourMode, Double]())((acc, modeCosts) => + tripExpectedMaxUtility(modeCosts, modeToTourMode).foreach { case (tourMode, util) => + acc += (tourMode -> (acc.getOrElse(tourMode, 0.0) + util)) + } + ) + x + } +} diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 191b32048a6..7875834679f 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -8,7 +8,7 @@ import beam.agentsim.agents._ import beam.agentsim.agents.household.HouseholdActor.{MobilityStatusInquiry, MobilityStatusResponse, ReleaseVehicle} import beam.agentsim.agents.modalbehaviors.ChoosesMode._ import beam.agentsim.agents.modalbehaviors.DrivesVehicle.{ActualVehicle, Token, VehicleOrToken} -import beam.agentsim.agents.planning.Strategy.ModeChoiceStrategy +import beam.agentsim.agents.planning.Strategy.{TourModeChoiceStrategy, TripModeChoiceStrategy} import beam.agentsim.agents.ridehail.{RideHailInquiry, RideHailRequest, RideHailResponse} import beam.agentsim.agents.vehicles.AccessErrorCodes.RideHailNotRequestedError import beam.agentsim.agents.vehicles.EnergyEconomyAttributes.Powertrain @@ -20,7 +20,8 @@ import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse, Zon import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, ScheduleTrigger} import beam.router.BeamRouter._ import beam.router.Modes.BeamMode -import beam.router.Modes.BeamMode.{WALK, _} +import beam.router.Modes.BeamMode._ +import beam.router.TourModes.BeamTourMode._ import beam.router.model.{BeamLeg, EmbodiedBeamLeg, EmbodiedBeamTrip} import beam.router.skim.core.ODSkimmer import beam.router.skim.event.ODSkimmerFailedTripEvent @@ -136,7 +137,9 @@ trait ChoosesMode { val choosesModeData: ChoosesModeData = nextStateData.asInstanceOf[ChoosesModeData] val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) val nextAct = nextActivity(choosesModeData.personData).get - val currentTourMode = _experiencedBeamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) + val currentTourStrategy = _experiencedBeamPlan.getTourStrategy[TourModeChoiceStrategy](nextAct) + val currentTripMode = _experiencedBeamPlan.getTripStrategy[TripModeChoiceStrategy](nextAct).mode + val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( choosesModeData.personData.currentTripMode, choosesModeData.personData, @@ -147,8 +150,8 @@ trait ChoosesMode { case data: ChoosesModeData if data.personData.currentTourPersonalVehicle.isDefined && ( - currentTourMode.exists(mode => mode == CAR || mode == BIKE) || - currentTourMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) + currentTripMode.exists(mode => mode == CAR || mode == BIKE) || + currentTripMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) && isLastTripWithinTour(data.personData, nextAct) ) => self ! MobilityStatusResponse( @@ -161,29 +164,33 @@ trait ChoosesMode { val vehicles = Vector(ActualVehicle(teleportationVehicle)) self ! MobilityStatusResponse(vehicles, getCurrentTriggerIdOrGenerate) // Only need to get available street vehicles if our mode requires such a vehicle - case data: ChoosesModeData if correctedCurrentTripMode.forall(Modes.isPersonalVehicleMode) => + case data: ChoosesModeData => implicit val executionContext: ExecutionContext = context.system.dispatcher - correctedCurrentTripMode match { - case Some(CAR | DRIVE_TRANSIT) => + currentTourStrategy.tourMode match { + case Some(CAR_BASED) => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData), Some(VehicleCategory.Car) ) pipeTo self - case Some(BIKE | BIKE_TRANSIT) => + case Some(BIKE_BASED) => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData), Some(VehicleCategory.Bike) ) pipeTo self - case _ => + // Drive/Bike transit tours count as WALK_BASED tours, + // but they only have access to their vehicles on first/last leg + case Some(WALK_BASED) | None + if isFirstTripWithinTour(data.personData, nextAct) || isLastTripWithinTour(data.personData, nextAct) => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData) ) pipeTo self + case _ => self ! MobilityStatusResponse(Vector(), getCurrentTriggerIdOrGenerate) } // Otherwise, send empty list to self @@ -1421,10 +1428,10 @@ trait ChoosesMode { val nextAct = nextActivity(data.personData).get val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) val currentTrip = _experiencedBeamPlan.getTripContaining(nextAct) - val tripStrategy = ModeChoiceStrategy(Some(chosenTrip.tripClassifier)) + val tripStrategy = TripModeChoiceStrategy(Some(chosenTrip.tripClassifier)) val currentTourStrategy = tripStrategy.tourStrategy(_experiencedBeamPlan, currentActivity(data.personData), nextAct) _experiencedBeamPlan.putStrategy(currentTrip, tripStrategy) - _experiencedBeamPlan.putStrategy(currentTour, currentTourStrategy) + _experiencedBeamPlan.putStrategy(currentTour, currentTourStrategy) // TODO: This doesn't need to be used anymore val modeChoiceEvent = new ModeChoiceEvent( tick, diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 6be4d59b132..bfbb42af137 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -3,8 +3,10 @@ package beam.agentsim.agents.planning import beam.agentsim.agents.planning.BeamPlan.atHome import java.{lang, util} -import beam.agentsim.agents.planning.Strategy.{ModeChoiceStrategy, Strategy} +import beam.agentsim.agents.planning.Strategy.{Strategy, TourModeChoiceStrategy, TripModeChoiceStrategy} import beam.router.Modes.BeamMode +import beam.router.TourModes.BeamTourMode +import beam.router.TourModes.BeamTourMode._ import org.matsim.api.core.v01.population._ import org.matsim.core.population.PopulationUtils import org.matsim.utils.objectattributes.attributable.Attributes @@ -120,8 +122,10 @@ class BeamPlan extends Plan { val nextTrip = Trip(activity, nextLeg, nextTour) nextTour.addTrip(nextTrip) if (atHome(activity)) { + // TODO: Also trigger this if we return to a location already present in the tour tours = tours :+ nextTour - nextTour = new Tour + putStrategy(nextTour, TourModeChoiceStrategy(getTourModeFromTourLegs(nextTour))) + nextTour = new Tour(originActivity = Some(activity)) } case leg: Leg => nextLeg = Some(leg) @@ -130,7 +134,7 @@ class BeamPlan extends Plan { indexBeamPlan() actsLegs.foreach { case l: Leg => - putStrategy(actsLegToTrip(l), ModeChoiceStrategy(BeamMode.fromString(l.getMode))) + putStrategy(actsLegToTrip(l), TripModeChoiceStrategy(BeamMode.fromString(l.getMode))) case _ => } } @@ -152,29 +156,30 @@ class BeamPlan extends Plan { val planElementMap = strategies.getOrElseUpdate(planElement, mutable.Map.empty[Class[_ <: Strategy], Strategy]) planElementMap.put(strategy.getClass, strategy) - planElement match { - case tour: Tour => - strategy.tripStrategies(tour, this).foreach { case (trip, strategy) => - putStrategy(trip, strategy) + (strategy, planElement) match { + case (tripModeChoiceStrategy: TripModeChoiceStrategy, tour: Tour) => + tripModeChoiceStrategy.tripStrategies(tour, this).foreach { case (trip, _) => + putStrategy(trip, tripModeChoiceStrategy) } - case trip: Trip => - putStrategy(trip.activity, strategy) - trip.leg.foreach(theLeg => putStrategy(theLeg, strategy)) + case (tripModeChoiceStrategy: TripModeChoiceStrategy, trip: Trip) => + putStrategy(trip.activity, tripModeChoiceStrategy) // I don't think this gets used + trip.leg.foreach(theLeg => putStrategy(theLeg, tripModeChoiceStrategy)) + case (_: TourModeChoiceStrategy, _: Trip) => + throw new RuntimeException("Can only set tour mode strategy from within a tour") case _ => - // Already dealt with Acts and Legs } } - def getStrategy[T <: Strategy: ClassTag](planElement: PlanElement): Option[T] = { + def getStrategy[T <: Strategy: ClassTag](planElement: PlanElement): T = { val forClass: Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]] - strategies.getOrElse(planElement, Map.empty[Class[_ <: Strategy], Strategy]).get(forClass).asInstanceOf[Option[T]] + strategies.getOrElse(planElement, Map.empty[Class[_ <: Strategy], Strategy]).get(forClass).asInstanceOf[T] } - def getTripStrategy[T <: Strategy: ClassTag](activity: Activity): Option[T] = { + def getTripStrategy[T <: Strategy: ClassTag](activity: Activity): T = { getStrategy(actsLegToTrip(activity)) } - def getTourStrategy[T <: Strategy: ClassTag](activity: Activity): Option[T] = { + def getTourStrategy[T <: Strategy: ClassTag](activity: Activity): T = { getStrategy(getTourContaining(activity)) } @@ -225,6 +230,21 @@ class BeamPlan extends Plan { getTripContaining(activities(index)) } + def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { + // TODO: Should this just look at the first/last mode of legs? + var tourMode: Option[BeamTourMode] = None + tour.trips.foreach(trip => + trip.leg match { + case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) + case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) => + tourMode = Some(BIKE_BASED) + case Some(_) => tourMode = Some(WALK_BASED) + case _ => + } + ) + tourMode + } + ////////////////////////////////////////////////////////////////////// // Supporting legacy interface ////////////////////////////////////////////////////////////////////// diff --git a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala index 05facbf6872..83175e81cac 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala @@ -1,8 +1,11 @@ package beam.agentsim.agents.planning import beam.agentsim.agents.planning.BeamPlan.atHome +import beam.agentsim.agents.vehicles.BeamVehicle import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode._ +import beam.router.TourModes.BeamTourMode +import org.matsim.api.core.v01.Id import org.matsim.api.core.v01.population.Activity /** @@ -22,7 +25,10 @@ object Strategy { def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = Seq.empty } - case class ModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy { + case class TourModeChoiceStrategy(tourMode: Option[BeamTourMode], tourVehicle: Option[Id[BeamVehicle]] = None) + extends Strategy + + case class TripModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy { override def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = { val tourStrategy = this @@ -40,7 +46,7 @@ object Strategy { //because these CAR_HOV modes shouldn't be used in this type of tour tour.trips .withFilter { trip => - beamPlan.getStrategy[ModeChoiceStrategy](trip).flatMap(_.mode) match { + beamPlan.getStrategy[TripModeChoiceStrategy](trip).mode match { case Some(CAR_HOV2 | CAR_HOV3) => true case _ => false } @@ -50,8 +56,8 @@ object Strategy { } } - def tourStrategy(beamPlan: BeamPlan, curAct: Activity, nextAct: Activity): ModeChoiceStrategy = { - val currentTourModeOpt = beamPlan.getTourStrategy[ModeChoiceStrategy](nextAct).flatMap(_.mode) + def tourStrategy(beamPlan: BeamPlan, curAct: Activity, nextAct: Activity): TripModeChoiceStrategy = { + val currentTourModeOpt = beamPlan.getTourStrategy[TripModeChoiceStrategy](nextAct).mode val newTourMode = currentTourModeOpt match { case Some(_) if mode.get.isHovTeleportation => mode case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => currentTourModeOpt @@ -59,7 +65,7 @@ object Strategy { case Some(_) => currentTourModeOpt case None => mode } - ModeChoiceStrategy(newTourMode) + TripModeChoiceStrategy(newTourMode) } } diff --git a/src/main/scala/beam/agentsim/agents/planning/Tour.scala b/src/main/scala/beam/agentsim/agents/planning/Tour.scala index 64ce976f211..68f5cd889ef 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Tour.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Tour.scala @@ -1,9 +1,12 @@ package beam.agentsim.agents.planning -import org.matsim.api.core.v01.population.PlanElement +import org.matsim.api.core.v01.population.{Activity, PlanElement} import org.matsim.utils.objectattributes.attributable.Attributes -class Tour(private var tripsInternal: Vector[Trip] = Vector()) extends PlanElement { +class Tour( + private var tripsInternal: Vector[Trip] = Vector(), + val originActivity: Option[Activity] = None +) extends PlanElement { def trips: Seq[Trip] = tripsInternal diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index c610f65a285..56efb067b0a 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -1,6 +1,6 @@ package beam.router -import beam.router.Modes.BeamMode.{BIKE, CAR, CAR_HOV2, CAR_HOV3, CAV, WALK} +import beam.agentsim.agents.vehicles.VehicleCategory._ import com.conveyal.r5.api.util.{LegMode, TransitModes} import com.conveyal.r5.profile.StreetMode import enumeratum.values._ @@ -202,11 +202,11 @@ object Modes { } def toR5StreetMode(mode: BeamMode): StreetMode = mode match { - case BIKE => StreetMode.BICYCLE - case WALK => StreetMode.WALK - case CAR => StreetMode.CAR - case CAV => StreetMode.CAR - case _ => throw new IllegalArgumentException + case BeamMode.BIKE => StreetMode.BICYCLE + case BeamMode.WALK => StreetMode.WALK + case BeamMode.CAR => StreetMode.CAR + case BeamMode.CAV => StreetMode.CAR + case _ => throw new IllegalArgumentException } def toR5StreetMode(mode: LegMode): StreetMode = mode match { @@ -245,3 +245,50 @@ object Modes { } } + +object TourModes { + import beam.router.Modes.BeamMode + import beam.router.Modes.BeamMode._ + + sealed abstract class BeamTourMode( + val value: String, + val vehicleCategory: VehicleCategory, + val allowedBeamModes: Seq[BeamMode] + ) extends StringEnumEntry { + + import BeamTourMode._ + + def isVehicleBased: Boolean = this match { + case WALK_BASED => false + case _ => true + } + } + + object BeamTourMode extends StringEnum[BeamTourMode] with StringCirceEnum[BeamTourMode] { + + override val values: immutable.IndexedSeq[BeamTourMode] = findValues + + // TODO: Also allow use of shared bikes/cars in walk based tours + case object WALK_BASED + extends BeamTourMode( + "walk_based", + Body, + Seq[BeamMode]( + WALK, + WALK_TRANSIT, + RIDE_HAIL, + RIDE_HAIL_POOLED, + RIDE_HAIL_TRANSIT, + DRIVE_TRANSIT, + BIKE_TRANSIT, + HOV2_TELEPORTATION, + HOV3_TELEPORTATION + ) + ) + + case object CAR_BASED extends BeamTourMode("car_based", Car, Seq[BeamMode](CAR, CAR_HOV2, CAR_HOV3)) + + case object BIKE_BASED extends BeamTourMode("bike_based", Car, Seq[BeamMode](BIKE)) + } + +} diff --git a/src/main/scala/beam/router/skim/core/ODSkimmer.scala b/src/main/scala/beam/router/skim/core/ODSkimmer.scala index d52d1a09f3b..62fa7d40193 100644 --- a/src/main/scala/beam/router/skim/core/ODSkimmer.scala +++ b/src/main/scala/beam/router/skim/core/ODSkimmer.scala @@ -16,6 +16,7 @@ import org.matsim.core.controler.MatsimServices import org.matsim.core.controler.events.IterationEndsEvent import org.apache.commons.lang3.math.NumberUtils +import scala.util.Try import scala.util.control.NonFatal class ODSkimmer @Inject() (matsimServices: MatsimServices, beamScenario: BeamScenario, beamConfig: BeamConfig) @@ -381,6 +382,24 @@ object ODSkimmer extends LazyLogging { override def toCsv: String = hour + "," + mode + "," + origin + "," + destination } + case class ODSkimmerTimeCostTransfer( + timeInHours: Double = 0.0, + cost: Double = 0.0, + numTransfers: Int = 0, + crowdingLevel: Double = 0.0 + ) { + + def +(other: ODSkimmerTimeCostTransfer): ODSkimmerTimeCostTransfer = { + ODSkimmerTimeCostTransfer( + this.timeInHours + other.timeInHours, + this.cost + other.cost, + this.numTransfers + other.numTransfers, + (Try(this.crowdingLevel / this.timeInHours).getOrElse(0) + Try(other.crowdingLevel / other.timeInHours) + .getOrElse(0)) * (this.timeInHours + other.timeInHours) + ) + } + } + def fromCsv( row: scala.collection.Map[String, String] ): (AbstractSkimmerKey, AbstractSkimmerInternal) = { diff --git a/src/main/scala/beam/router/skim/readonly/ODSkims.scala b/src/main/scala/beam/router/skim/readonly/ODSkims.scala index 0e80ed3ba3f..559f12da9b9 100644 --- a/src/main/scala/beam/router/skim/readonly/ODSkims.scala +++ b/src/main/scala/beam/router/skim/readonly/ODSkims.scala @@ -1,6 +1,7 @@ package beam.router.skim.readonly import beam.agentsim.agents.choice.mode.DrivingCost +import beam.agentsim.agents.planning.Tour import beam.agentsim.agents.vehicles.BeamVehicleType import beam.agentsim.infrastructure.taz.TAZ import beam.router.BeamRouter @@ -17,9 +18,10 @@ import beam.router.Modes.BeamMode.{ TRANSIT, WALK_TRANSIT } +import beam.router.TourModes.BeamTourMode import beam.router.skim.SkimsUtils.{distanceAndTime, getRideHailCost, timeToBin} import beam.router.skim.core.AbstractSkimmerReadOnly -import beam.router.skim.core.ODSkimmer.{ExcerptData, ODSkimmerInternal, ODSkimmerKey, Skim} +import beam.router.skim.core.ODSkimmer.{ExcerptData, ODSkimmerInternal, ODSkimmerKey, ODSkimmerTimeCostTransfer, Skim} import beam.sim.config.BeamConfig import beam.sim.{BeamHelper, BeamScenario, BeamServices} import org.matsim.api.core.v01.{Coord, Id} @@ -97,6 +99,35 @@ class ODSkims(beamConfig: BeamConfig, beamScenario: BeamScenario) extends Abstra (timeFactor, costFactor) } + def getTourModeCosts( + modes: Seq[BeamMode], + tour: Tour, + vehicleTypeId: Id[BeamVehicleType], + vehicleType: BeamVehicleType, + fuelPrice: Double + ): Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]] = { + tour.originActivity match { + case Some(originActivity) => + var tripOrigin = originActivity + tour.trips.map { trip => + modes.map { mode => + val skim = getTimeDistanceAndCost( + tripOrigin.getCoord, + trip.activity.getCoord, + originActivity.getEndTime.toInt, + mode, + vehicleTypeId, + vehicleType, + fuelPrice + ) + tripOrigin = trip.activity + mode -> ODSkimmerTimeCostTransfer(skim.time / 3600, skim.cost, 0, 0) + }.toMap + } + case _ => Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]]() + } + } + def getTimeDistanceAndCost( originUTM: Location, destinationUTM: Location, diff --git a/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala b/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala index b3866df31e0..9584169840b 100755 --- a/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala +++ b/src/test/scala/beam/agentsim/planning/BeamPlanSpec.scala @@ -1,7 +1,7 @@ package beam.agentsim.planning import beam.agentsim.agents.planning.BeamPlan -import beam.agentsim.agents.planning.Strategy.ModeChoiceStrategy +import beam.agentsim.agents.planning.Strategy.TripModeChoiceStrategy import beam.router.Modes.BeamMode.CAR import beam.sim.BeamHelper import org.matsim.api.core.v01.Coord @@ -37,7 +37,7 @@ class BeamPlanSpec extends AnyWordSpecLike with Matchers with BeamHelper { PopulationUtils.createAndAddLeg(matsimPlan, "car") PopulationUtils.createAndAddActivityFromCoord(matsimPlan, "Home", new Coord(0.0, 0.0)) - val strat = ModeChoiceStrategy(Some(CAR)) + val strat = TripModeChoiceStrategy(Some(CAR)) "should contain the same activities and legs as the MATSimn plan used in creation" in { val beamPlan = BeamPlan(matsimPlan) @@ -52,49 +52,49 @@ class BeamPlanSpec extends AnyWordSpecLike with Matchers with BeamHelper { val beamPlan = BeamPlan(matsimPlan) val act = beamPlan.activities.head beamPlan.putStrategy(act, strat) - beamPlan.getStrategy[ModeChoiceStrategy](act) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](act) should be(Some(strat)) } "should attach a strategy to a leg" in { val beamPlan = BeamPlan(matsimPlan) val leg = beamPlan.legs.head beamPlan.putStrategy(leg, strat) - beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](leg) should be(Some(strat)) } "should attach a strategy to a trip" in { val beamPlan = BeamPlan(matsimPlan) val trip = beamPlan.trips.head beamPlan.putStrategy(trip, strat) - beamPlan.getStrategy[ModeChoiceStrategy](trip) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](trip) should be(Some(strat)) } "should attach a strategy to a tour" in { val beamPlan = BeamPlan(matsimPlan) val tour = beamPlan.tours.head beamPlan.putStrategy(tour, strat) - beamPlan.getStrategy[ModeChoiceStrategy](tour) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](tour) should be(Some(strat)) } "should attach a strategy to a trip and the trip's activity and leg" in { val beamPlan = BeamPlan(matsimPlan) val trip = beamPlan.trips.head beamPlan.putStrategy(trip, strat) - beamPlan.getStrategy[ModeChoiceStrategy](trip.activity) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](trip.activity) should be(Some(strat)) trip.leg match { case Some(leg) => - beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](leg) should be(Some(strat)) case None => } } "should not attach a strategy to tour's trips, activities, and legs" in { val beamPlan = BeamPlan(matsimPlan) val tour = beamPlan.tours(1) - val strategy = ModeChoiceStrategy(None) + val strategy = TripModeChoiceStrategy(None) beamPlan.putStrategy(tour, strategy) - beamPlan.getStrategy[ModeChoiceStrategy](tour) should be(Some(strategy)) + beamPlan.getStrategy[TripModeChoiceStrategy](tour) should be(Some(strategy)) tour.trips.foreach { trip => - beamPlan.getStrategy[ModeChoiceStrategy](trip) should be(Some(strat)) - beamPlan.getStrategy[ModeChoiceStrategy](trip.activity) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](trip) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](trip.activity) should be(Some(strat)) trip.leg match { case Some(leg) => - beamPlan.getStrategy[ModeChoiceStrategy](leg) should be(Some(strat)) + beamPlan.getStrategy[TripModeChoiceStrategy](leg) should be(Some(strat)) case None => } } From 9aaf8d3d99c5a5d0e9d51e208718316a867a8fe2 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Wed, 1 Jun 2022 19:31:36 -0700 Subject: [PATCH 17/40] fix --- .../mode/TourModeChoiceMultinomialLogit.scala | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala index 653e8640d9f..76492a8fe42 100644 --- a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala @@ -36,7 +36,9 @@ class TourModeChoiceMultinomialLogit( val skims = tripModeCosts.getOrElse(beamMode, ODSkimmerTimeCostTransfer()) val timeCost = attributesOfIndividual.getVOT(skims.timeInHours) val monetaryCost = skims.cost - beamMode -> (Map("cost" -> (timeCost + monetaryCost)) ++ Map("transfers" -> skims.numTransfers)) + beamMode -> (Map("cost" -> (timeCost + monetaryCost)) ++ Map( + "transfers" -> skims.numTransfers.toDouble + )) }.toMap beamTourMode -> modeLogit.getExpectedMaximumUtility(modeChoice).getOrElse(Double.NegativeInfinity) } @@ -46,11 +48,13 @@ class TourModeChoiceMultinomialLogit( tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] ): Map[BeamTourMode, Double] = { - val x = tourModeCosts.foldLeft(mutable.Map[BeamTourMode, Double]())((acc, modeCosts) => - tripExpectedMaxUtility(modeCosts, modeToTourMode).foreach { case (tourMode, util) => - acc += (tourMode -> (acc.getOrElse(tourMode, 0.0) + util)) + val tourModeToExpectedUtility = mutable.Map[BeamTourMode, Double]() + tourModeCosts foreach { modeCosts => + val tourModeExpectedCosts = tripExpectedMaxUtility(modeCosts, modeToTourMode) + tourModeExpectedCosts.map { case (tourMode, util) => + tourModeToExpectedUtility += (tourMode -> (tourModeToExpectedUtility.getOrElse(tourMode, 0.0) + util)) } - ) - x + } + tourModeToExpectedUtility.toMap } } From 17f54148452b7a3d6d3106937644eb2e327e9ead Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Thu, 2 Jun 2022 17:26:01 -0700 Subject: [PATCH 18/40] Do tour mode choice in ChoosesMode --- .../beam/agentsim/agents/PersonAgent.scala | 6 +- .../choice/logit/TourModeChoiceModel.scala | 5 -- .../mode/ModeChoiceMultinomialLogit.scala | 2 + .../mode/TourModeChoiceMultinomialLogit.scala | 77 +++++++++++++++---- .../agents/household/HouseholdActor.scala | 5 ++ .../agents/modalbehaviors/ChoosesMode.scala | 31 ++++++++ .../modalbehaviors/ModeChoiceCalculator.scala | 14 +++- .../agentsim/agents/planning/BeamPlan.scala | 5 +- .../agentsim/agents/planning/Strategy.scala | 9 ++- src/main/scala/beam/router/Modes.scala | 27 ++++++- .../beam/router/skim/core/ODSkimmer.scala | 8 +- 11 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 8b8fcca7862..a9acdd243fe 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -5,6 +5,7 @@ import akka.actor.{ActorRef, FSM, Props, Stash, Status} import beam.agentsim.Resource._ import beam.agentsim.agents.BeamAgent._ import beam.agentsim.agents.PersonAgent._ +import beam.agentsim.agents.choice.mode.TourModeChoiceMultinomialLogit import beam.agentsim.agents.freight.input.FreightReader.PAYLOAD_WEIGHT_IN_KG import beam.agentsim.agents.household.HouseholdActor.ReleaseVehicle import beam.agentsim.agents.household.HouseholdCAVDriverAgent @@ -87,6 +88,7 @@ object PersonAgent { services: BeamServices, beamScenario: BeamScenario, modeChoiceCalculator: ModeChoiceCalculator, + tourModeChoiceCalculator: TourModeChoiceMultinomialLogit, transportNetwork: TransportNetwork, tollCalculator: TollCalculator, router: ActorRef, @@ -108,6 +110,7 @@ object PersonAgent { services, beamScenario, modeChoiceCalculator, + tourModeChoiceCalculator, transportNetwork, router, rideHailManager, @@ -296,6 +299,7 @@ class PersonAgent( val beamServices: BeamServices, val beamScenario: BeamScenario, val modeChoiceCalculator: ModeChoiceCalculator, + val tourModeChoiceCalculator: TourModeChoiceMultinomialLogit, val transportNetwork: TransportNetwork, val router: ActorRef, val rideHailManager: ActorRef, @@ -615,7 +619,7 @@ class PersonAgent( // If we have the currentTourPersonalVehicle then we should use it // use the mode of the next leg as the new trip mode. currentTripMode = modeOfNextLeg, - currentTourMode = currentTourModeChoiceStrategy.tourMode, + currentTourMode = None, // currentTourModeChoiceStrategy.tourMode, numberOfReplanningAttempts = 0, failedTrips = IndexedSeq.empty, enrouteData = EnrouteData() diff --git a/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala b/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala index c4855041951..6e507d28c13 100644 --- a/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala +++ b/src/main/scala/beam/agentsim/agents/choice/logit/TourModeChoiceModel.scala @@ -1,11 +1,6 @@ package beam.agentsim.agents.choice.logit import beam.sim.config.BeamConfig -import beam.sim.population.AttributesOfIndividual -import org.matsim.api.core.v01.population.Activity - -import scala.collection.JavaConverters._ -import scala.util.{Failure, Success, Try} object TourModeChoiceModel { def apply(beamConfig: BeamConfig) = new TourModeChoiceModel(beamConfig) diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala index bad3a15e1d0..ffbcd566c2a 100755 --- a/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala @@ -44,6 +44,8 @@ class ModeChoiceMultinomialLogit( override lazy val beamConfig: BeamConfig = beamConfigHolder.beamConfig + override val modeChoiceLogit: MultinomialLogit[BeamMode, String] = modeModel + var expectedMaximumUtility: Double = 0.0 val modalBehaviors: ModalBehaviors = beamConfig.beam.agentsim.agents.modalBehaviors diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala index 76492a8fe42..757c4cad648 100644 --- a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala @@ -1,10 +1,10 @@ package beam.agentsim.agents.choice.mode +import beam.agentsim.agents.choice.logit.{MultinomialLogit, TourModeChoiceModel, UtilityFunctionOperation} +import beam.agentsim.agents.choice.logit.TourModeChoiceModel.{TourModeMNLConfig, TourModeParameters} import beam.router.Modes.BeamMode import beam.router.TourModes.BeamTourMode -import beam.router.TourModes.BeamTourMode.WALK_BASED import beam.router.skim.core.ODSkimmer.ODSkimmerTimeCostTransfer -import beam.sim.config.BeamConfigHolder import beam.sim.population.AttributesOfIndividual import scala.collection.mutable @@ -12,23 +12,29 @@ import scala.util.Random class TourModeChoiceMultinomialLogit( val attributesOfIndividual: AttributesOfIndividual, - val configHolder: BeamConfigHolder + val tourModeChoiceModel: TourModeChoiceModel ) { val rnd: Random = new scala.util.Random(System.currentTimeMillis()) - val (_, modeLogit) = ModeChoiceMultinomialLogit.buildModelFromConfig(configHolder) + val tourModeLogit = MultinomialLogit[BeamTourMode, TourModeChoiceModel.TourModeParameters]( + Map.empty[BeamTourMode, Map[TourModeParameters, UtilityFunctionOperation]], + tourModeChoiceModel.DefaultMNLParameters + ) def chooseTourMode( tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], + modeLogit: MultinomialLogit[BeamMode, String], modeToTourMode: Map[BeamTourMode, Seq[BeamMode]], - firstAndLastTripModeToTourMode: Option[Map[BeamTourMode, Seq[BeamMode]]] - ): BeamTourMode = { - - WALK_BASED + firstAndLastTripModeToTourModeOption: Option[Map[BeamTourMode, Seq[BeamMode]]] + ): Option[BeamTourMode] = { + val tourUtility = + tourExpectedMaxUtility(tourModeCosts, modeLogit, modeToTourMode, firstAndLastTripModeToTourModeOption) + tourModeChoice(tourUtility, tourModeLogit) } def tripExpectedMaxUtility( tripModeCosts: Map[BeamMode, ODSkimmerTimeCostTransfer], + modeLogit: MultinomialLogit[BeamMode, String], modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] ): Map[BeamTourMode, Double] = { modeToTourMode map { case (beamTourMode, beamModes) => @@ -46,15 +52,60 @@ class TourModeChoiceMultinomialLogit( def tourExpectedMaxUtility( tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], - modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] + modeLogit: MultinomialLogit[BeamMode, String], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]], + firstAndLastTripModeToTourModeOption: Option[Map[BeamTourMode, Seq[BeamMode]]] = None ): Map[BeamTourMode, Double] = { val tourModeToExpectedUtility = mutable.Map[BeamTourMode, Double]() - tourModeCosts foreach { modeCosts => - val tourModeExpectedCosts = tripExpectedMaxUtility(modeCosts, modeToTourMode) - tourModeExpectedCosts.map { case (tourMode, util) => - tourModeToExpectedUtility += (tourMode -> (tourModeToExpectedUtility.getOrElse(tourMode, 0.0) + util)) + tourModeCosts.zipWithIndex foreach { case (modeCosts, idx) => + if (idx == 0 | idx == tourModeCosts.length - 1) { + // Allow inclusion of private vehicles in first/last trips, e.g. for DRIVE_TRANSIT + // Default to normal modeToTourMode if the other mapping isn't defined, though + tripExpectedMaxUtility(modeCosts, modeLogit, firstAndLastTripModeToTourModeOption.getOrElse(modeToTourMode)) + .map { case (tourMode, util) => + tourModeToExpectedUtility += (tourMode -> (tourModeToExpectedUtility.getOrElse(tourMode, 0.0) + util)) + } + } else { + tripExpectedMaxUtility(modeCosts, modeLogit, modeToTourMode).map { case (tourMode, util) => + tourModeToExpectedUtility += (tourMode -> (tourModeToExpectedUtility.getOrElse(tourMode, 0.0) + util)) + } } + } tourModeToExpectedUtility.toMap } + + def tourModeChoice( + tourModeUtility: Map[BeamTourMode, Double], + tourModeLogit: MultinomialLogit[BeamTourMode, TourModeParameters] + ): Option[BeamTourMode] = { + val tourModeChoiceData: Map[BeamTourMode, Map[TourModeParameters, Double]] = tourModeUtility.map { + case (tourMode, util) => + tourMode -> Map[TourModeParameters, Double]( + TourModeParameters.ExpectedMaxUtility -> util, + TourModeParameters.Intercept -> 0 + ) + } + tourModeLogit.sampleAlternative(tourModeChoiceData, rnd) match { + case Some(sample) => Some(sample.alternativeType) + case None => None + } + } + + def apply( + tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], + modeLogit: MultinomialLogit[BeamMode, String], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]], + firstAndLastTripModeToTourModeOption: Option[Map[BeamTourMode, Seq[BeamMode]]] + ): Option[BeamTourMode] = { + chooseTourMode(tourModeCosts, modeLogit, modeToTourMode, firstAndLastTripModeToTourModeOption) + } + + def apply( + tourModeCosts: Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]], + modeLogit: MultinomialLogit[BeamMode, String], + modeToTourMode: Map[BeamTourMode, Seq[BeamMode]] + ): Option[BeamTourMode] = { + chooseTourMode(tourModeCosts, modeLogit, modeToTourMode, None) + } } diff --git a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala index 37f09f1c98e..e05646f15b6 100755 --- a/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala +++ b/src/main/scala/beam/agentsim/agents/household/HouseholdActor.scala @@ -7,6 +7,8 @@ import akka.util.Timeout import beam.agentsim.Resource.NotifyVehicleIdle import beam.agentsim.agents.BeamAgent.Finish import beam.agentsim.agents._ +import beam.agentsim.agents.choice.logit.TourModeChoiceModel +import beam.agentsim.agents.choice.mode.TourModeChoiceMultinomialLogit import beam.agentsim.agents.modalbehaviors.ChoosesMode.{CavTripLegsRequest, CavTripLegsResponse} import beam.agentsim.agents.modalbehaviors.DrivesVehicle.VehicleOrToken import beam.agentsim.agents.modalbehaviors.ModeChoiceCalculator @@ -345,6 +347,8 @@ object HouseholdActor { household.members.foreach { person => val attributes = person.getCustomAttributes.get("beam-attributes").asInstanceOf[AttributesOfIndividual] val modeChoiceCalculator = modeChoiceCalculatorFactory(attributes) + val tourModeChoiceCalculator = + new TourModeChoiceMultinomialLogit(attributes, new TourModeChoiceModel(beamScenario.beamConfig)) val selectedPlan = person.getSelectedPlan // Set zero endTime for plans with one activity. In other case agent sim will be started // before all InitializeTrigger's are completed @@ -361,6 +365,7 @@ object HouseholdActor { beamServices, beamScenario, modeChoiceCalculator, + tourModeChoiceCalculator, transportNetwork, tollCalculator, router, diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 7875834679f..391dacdf22c 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -21,6 +21,7 @@ import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, ScheduleTri import beam.router.BeamRouter._ import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode._ +import beam.router.TourModes.BeamTourMode import beam.router.TourModes.BeamTourMode._ import beam.router.model.{BeamLeg, EmbodiedBeamLeg, EmbodiedBeamTrip} import beam.router.skim.core.ODSkimmer @@ -266,6 +267,36 @@ trait ChoosesMode { Vector() } + val chosenCurrentTourMode: Option[BeamTourMode] = personData.currentTourMode match { + case Some(tourMode) => Some(tourMode) + case None => + val availablePersonalVehicleModes = availablePersonalStreetVehicles.map(x => x.streetVehicle.mode).distinct + val availableFirstAndLastLegModes = + availablePersonalVehicleModes.flatMap(x => BeamTourMode.enabledModes.get(x)).flatten + val modesToQuery = + (availablePersonalVehicleModes ++ BeamMode.nonPersonalVehicleModes ++ availableFirstAndLastLegModes).distinct + val dummyVehicleType = beamScenario.vehicleTypes(dummyRHVehicle.vehicleTypeId) + val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) + val tourModeCosts = beamServices.skims.od_skimmer.getTourModeCosts( + modesToQuery, + currentTour, + dummyRHVehicle.vehicleTypeId, + dummyVehicleType, + beamScenario.fuelTypePrices(dummyVehicleType.primaryFuelType) + ) + val modeToTourMode = + BeamTourMode.values.map(tourMode => tourMode -> tourMode.allowedBeamModes.intersect(modesToQuery)).toMap + val firstAndLastTripModeToTourModeOption = BeamTourMode.values + .map(tourMode => tourMode -> tourMode.allowedBeamModesForFirstAndLastLeg.intersect(modesToQuery)) + .toMap + tourModeChoiceCalculator( + tourModeCosts, + modeChoiceCalculator.modeChoiceLogit, + modeToTourMode, + Some(firstAndLastTripModeToTourModeOption) + ) + } + def makeRequestWith( withTransit: Boolean, vehicles: Vector[StreetVehicle], diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala index 4f6699c47c9..07d7d244b32 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala @@ -1,11 +1,14 @@ package beam.agentsim.agents.modalbehaviors -import beam.agentsim.agents.choice.logit.LatentClassChoiceModel +import beam.agentsim.agents.choice.logit +import beam.agentsim.agents.choice.logit.{LatentClassChoiceModel, UtilityFunctionOperation} import beam.agentsim.agents.choice.logit.LatentClassChoiceModel.Mandatory +import beam.agentsim.agents.choice.logit.TourModeChoiceModel.TourModeParameters import beam.agentsim.agents.choice.mode._ import beam.agentsim.agents.vehicles.BeamVehicleType import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode._ +import beam.router.TourModes.BeamTourMode import beam.router.model.{EmbodiedBeamLeg, EmbodiedBeamTrip} import beam.sim.BeamServices import beam.sim.config.{BeamConfig, BeamConfigHolder} @@ -24,6 +27,15 @@ trait ModeChoiceCalculator { val beamConfig: BeamConfig + val commonUtility: Map[String, UtilityFunctionOperation] = Map( + "cost" -> UtilityFunctionOperation("multiplier", -1) + ) + + val modeChoiceLogit = new logit.MultinomialLogit[BeamMode, String]( + Map.empty[BeamMode, Option[Map[String, UtilityFunctionOperation]]], + commonUtility + ) + lazy val random: Random = new Random( beamConfig.matsim.modules.global.randomSeed ) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index bfbb42af137..882b8630eae 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -172,7 +172,10 @@ class BeamPlan extends Plan { def getStrategy[T <: Strategy: ClassTag](planElement: PlanElement): T = { val forClass: Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]] - strategies.getOrElse(planElement, Map.empty[Class[_ <: Strategy], Strategy]).get(forClass).asInstanceOf[T] + strategies + .getOrElse(planElement, Map.empty[Class[_ <: Strategy], Strategy]) + .getOrElse(forClass, forClass.getConstructor().newInstance()) + .asInstanceOf[T] } def getTripStrategy[T <: Strategy: ClassTag](activity: Activity): T = { diff --git a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala index 83175e81cac..d8f1c1fdad5 100755 --- a/src/main/scala/beam/agentsim/agents/planning/Strategy.scala +++ b/src/main/scala/beam/agentsim/agents/planning/Strategy.scala @@ -25,10 +25,13 @@ object Strategy { def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = Seq.empty } - case class TourModeChoiceStrategy(tourMode: Option[BeamTourMode], tourVehicle: Option[Id[BeamVehicle]] = None) - extends Strategy + case class TourModeChoiceStrategy(tourMode: Option[BeamTourMode] = None, tourVehicle: Option[Id[BeamVehicle]] = None) + extends Strategy { + def this() = this(None) + } - case class TripModeChoiceStrategy(mode: Option[BeamMode]) extends Strategy { + case class TripModeChoiceStrategy(mode: Option[BeamMode] = None) extends Strategy { + def this() = this(None) override def tripStrategies(tour: Tour, beamPlan: BeamPlan): Seq[(Trip, Strategy)] = { val tourStrategy = this diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 56efb067b0a..8c825366fb3 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -127,6 +127,8 @@ object Modes { val personalVehicleModes = Seq(CAR, BIKE, DRIVE_TRANSIT, BIKE_TRANSIT) + val nonPersonalVehicleModes = Seq(WALK, RIDE_HAIL, RIDE_HAIL_POOLED, RIDE_HAIL_TRANSIT, WALK_TRANSIT) + val transitModes = Seq(BUS, FUNICULAR, GONDOLA, CABLE_CAR, FERRY, TRAM, TRANSIT, RAIL, SUBWAY) @@ -253,7 +255,8 @@ object TourModes { sealed abstract class BeamTourMode( val value: String, val vehicleCategory: VehicleCategory, - val allowedBeamModes: Seq[BeamMode] + val allowedBeamModes: Seq[BeamMode], + val allowedBeamModesForFirstAndLastLeg: Seq[BeamMode] ) extends StringEnumEntry { import BeamTourMode._ @@ -268,11 +271,23 @@ object TourModes { override val values: immutable.IndexedSeq[BeamTourMode] = findValues + val enabledModes: Map[BeamMode, Seq[BeamMode]] = + Map[BeamMode, Seq[BeamMode]](CAR -> Seq(DRIVE_TRANSIT), BIKE -> Seq(BIKE_TRANSIT)) + // TODO: Also allow use of shared bikes/cars in walk based tours case object WALK_BASED extends BeamTourMode( "walk_based", Body, + Seq[BeamMode]( + WALK, + WALK_TRANSIT, + RIDE_HAIL, + RIDE_HAIL_POOLED, + RIDE_HAIL_TRANSIT, + HOV2_TELEPORTATION, + HOV3_TELEPORTATION + ), Seq[BeamMode]( WALK, WALK_TRANSIT, @@ -286,9 +301,15 @@ object TourModes { ) ) - case object CAR_BASED extends BeamTourMode("car_based", Car, Seq[BeamMode](CAR, CAR_HOV2, CAR_HOV3)) + case object CAR_BASED + extends BeamTourMode( + "car_based", + Car, + Seq[BeamMode](CAR, CAR_HOV2, CAR_HOV3), + Seq[BeamMode](CAR, CAR_HOV2, CAR_HOV3) + ) - case object BIKE_BASED extends BeamTourMode("bike_based", Car, Seq[BeamMode](BIKE)) + case object BIKE_BASED extends BeamTourMode("bike_based", Car, Seq[BeamMode](BIKE), Seq[BeamMode](BIKE)) } } diff --git a/src/main/scala/beam/router/skim/core/ODSkimmer.scala b/src/main/scala/beam/router/skim/core/ODSkimmer.scala index 62fa7d40193..b660d88c2f0 100644 --- a/src/main/scala/beam/router/skim/core/ODSkimmer.scala +++ b/src/main/scala/beam/router/skim/core/ODSkimmer.scala @@ -394,8 +394,12 @@ object ODSkimmer extends LazyLogging { this.timeInHours + other.timeInHours, this.cost + other.cost, this.numTransfers + other.numTransfers, - (Try(this.crowdingLevel / this.timeInHours).getOrElse(0) + Try(other.crowdingLevel / other.timeInHours) - .getOrElse(0)) * (this.timeInHours + other.timeInHours) + if (this.timeInHours <= 0) { other.crowdingLevel } + else if (other.timeInHours <= 0) { this.crowdingLevel } + else { + (this.crowdingLevel / this.timeInHours + other.crowdingLevel / other.timeInHours) * + (this.timeInHours + other.timeInHours) + } ) } } From 6bfde39c4e9e1e1ec6cff44ac38ba6bca8266635 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Fri, 3 Jun 2022 10:15:34 -0700 Subject: [PATCH 19/40] get correct tour for initial trip --- .../beam/agentsim/agents/PersonAgent.scala | 16 +++++++-------- .../agents/modalbehaviors/ChoosesMode.scala | 11 +++++----- .../agentsim/agents/planning/BeamPlan.scala | 20 ++++++++++--------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index a9acdd243fe..ab4a1dfe193 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -483,23 +483,23 @@ class PersonAgent( } } - def isFirstTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { - val (tripIndexOfElement: Int, _) = currentTripIndexWithinTour(personData, nextAct) + def isFirstTripWithinTour(nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, _) = currentTripIndexWithinTour(nextAct) tripIndexOfElement == 0 } - def isLastTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { - val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) + def isLastTripWithinTour(nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(nextAct) tripIndexOfElement == lastTripIndex } - def isFirstOrLastTripWithinTour(personData: BasePersonData, nextAct: Activity): Boolean = { - val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) + def isFirstOrLastTripWithinTour(nextAct: Activity): Boolean = { + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(nextAct) tripIndexOfElement == 0 || tripIndexOfElement == lastTripIndex } - def currentTripIndexWithinTour(personData: BasePersonData, nextAct: Activity): (Int, Int) = { - val tour = currentTour(personData) + def currentTripIndexWithinTour(nextAct: Activity): (Int, Int) = { + val tour = _experiencedBeamPlan.getTourContaining(nextAct) val lastTripIndex = tour.trips.size - 1 val tripIndexOfElement = tour .tripIndexOfElement(nextAct) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 391dacdf22c..1a8348bb407 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -153,7 +153,7 @@ trait ChoosesMode { ( currentTripMode.exists(mode => mode == CAR || mode == BIKE) || currentTripMode.exists(mode => mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT) - && isLastTripWithinTour(data.personData, nextAct) + && isLastTripWithinTour(nextAct) ) => self ! MobilityStatusResponse( Vector(beamVehicles(data.personData.currentTourPersonalVehicle.get)), @@ -184,8 +184,7 @@ trait ChoosesMode { ) pipeTo self // Drive/Bike transit tours count as WALK_BASED tours, // but they only have access to their vehicles on first/last leg - case Some(WALK_BASED) | None - if isFirstTripWithinTour(data.personData, nextAct) || isLastTripWithinTour(data.personData, nextAct) => + case Some(WALK_BASED) | None if isFirstTripWithinTour(nextAct) || isLastTripWithinTour(nextAct) => requestAvailableVehicles( vehicleFleets, data.currentLocation, @@ -258,7 +257,7 @@ trait ChoosesMode { // In these cases, also include teleportation vehicles newlyAvailableBeamVehicles case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => - if (isFirstOrLastTripWithinTour(personData, nextAct)) { + if (isFirstOrLastTripWithinTour(nextAct)) { newlyAvailableBeamVehicles } else { Vector() @@ -452,7 +451,7 @@ trait ChoosesMode { } case Some(mode @ (DRIVE_TRANSIT | BIKE_TRANSIT)) => val vehicleMode = Modes.getAccessVehicleMode(mode) - val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(personData, nextAct) + val (tripIndexOfElement: Int, lastTripIndex: Int) = currentTripIndexWithinTour(nextAct) ( tripIndexOfElement, personData.currentTourPersonalVehicle @@ -1219,7 +1218,7 @@ trait ChoosesMode { val filteredItinerariesForChoice = (choosesModeData.personData.currentTripMode match { case Some(mode) if mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT => - (isFirstOrLastTripWithinTour(personData, nextAct), personData.hasDeparted) match { + (isFirstOrLastTripWithinTour(nextAct), personData.hasDeparted) match { case (true, false) => combinedItinerariesForChoice.filter(_.tripClassifier == mode) case _ => diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 882b8630eae..fd281d9464d 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -236,15 +236,17 @@ class BeamPlan extends Plan { def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { // TODO: Should this just look at the first/last mode of legs? var tourMode: Option[BeamTourMode] = None - tour.trips.foreach(trip => - trip.leg match { - case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) - case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) => - tourMode = Some(BIKE_BASED) - case Some(_) => tourMode = Some(WALK_BASED) - case _ => - } - ) + if (tour.trips.exists(trip => trip.leg.isDefined)) { + tour.trips.foreach(trip => + trip.leg match { + case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) + case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) => + tourMode = Some(BIKE_BASED) + case Some(_) => tourMode = Some(WALK_BASED) + case _ => + } + ) + } tourMode } From e435a3b1eb60f6be5a05466a7fc271c2fb97e30b Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Fri, 3 Jun 2022 12:59:12 -0700 Subject: [PATCH 20/40] fixes for weird tour structure, int conversion --- .../mode/TourModeChoiceMultinomialLogit.scala | 2 +- .../agents/modalbehaviors/ChoosesMode.scala | 4 +- .../agentsim/agents/planning/BeamPlan.scala | 18 +++++-- .../beam/router/skim/readonly/ODSkims.scala | 47 ++++++++++++------- 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala index 757c4cad648..9ddd972ab96 100644 --- a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala @@ -42,7 +42,7 @@ class TourModeChoiceMultinomialLogit( val skims = tripModeCosts.getOrElse(beamMode, ODSkimmerTimeCostTransfer()) val timeCost = attributesOfIndividual.getVOT(skims.timeInHours) val monetaryCost = skims.cost - beamMode -> (Map("cost" -> (timeCost + monetaryCost)) ++ Map( + beamMode -> (Map("cost" -> -(timeCost + monetaryCost)) ++ Map( "transfers" -> skims.numTransfers.toDouble )) }.toMap diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 1a8348bb407..65716279dfb 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -288,12 +288,14 @@ trait ChoosesMode { val firstAndLastTripModeToTourModeOption = BeamTourMode.values .map(tourMode => tourMode -> tourMode.allowedBeamModesForFirstAndLastLeg.intersect(modesToQuery)) .toMap - tourModeChoiceCalculator( + val done = tourModeChoiceCalculator( tourModeCosts, modeChoiceCalculator.modeChoiceLogit, modeToTourMode, Some(firstAndLastTripModeToTourModeOption) ) + println(done) + done } def makeRequestWith( diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index fd281d9464d..12372047883 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -34,11 +34,19 @@ object BeamPlan { def apply(matsimPlan: Plan): BeamPlan = { val beamPlan = new BeamPlan beamPlan.setPerson(matsimPlan.getPerson) - matsimPlan.getPlanElements.asScala.foreach { - case activity: Activity => - beamPlan.addActivity(activity) - case leg: Leg => - beamPlan.addLeg(leg) + matsimPlan.getPlanElements.asScala.headOption match { + case Some(a1: Activity) => beamPlan.addActivity(a1) + case _ => + } + matsimPlan.getPlanElements.asScala.sliding(2).foreach { + case mutable.Buffer(_: Activity, a2: Activity) => + beamPlan.addLeg(PopulationUtils.createLeg("")) + beamPlan.addActivity(a2) + case mutable.Buffer(a1: Activity, l1: Leg) => + beamPlan.addLeg(l1) + case mutable.Buffer(l1: Leg, a1: Activity) => + beamPlan.addActivity(a1) + case _ => } beamPlan.setScore(matsimPlan.getScore) beamPlan.setType(matsimPlan.getType) diff --git a/src/main/scala/beam/router/skim/readonly/ODSkims.scala b/src/main/scala/beam/router/skim/readonly/ODSkims.scala index 559f12da9b9..b043c61527e 100644 --- a/src/main/scala/beam/router/skim/readonly/ODSkims.scala +++ b/src/main/scala/beam/router/skim/readonly/ODSkims.scala @@ -24,6 +24,7 @@ import beam.router.skim.core.AbstractSkimmerReadOnly import beam.router.skim.core.ODSkimmer.{ExcerptData, ODSkimmerInternal, ODSkimmerKey, ODSkimmerTimeCostTransfer, Skim} import beam.sim.config.BeamConfig import beam.sim.{BeamHelper, BeamScenario, BeamServices} +import org.matsim.api.core.v01.population.Activity import org.matsim.api.core.v01.{Coord, Id} import scala.collection.immutable @@ -108,26 +109,40 @@ class ODSkims(beamConfig: BeamConfig, beamScenario: BeamScenario) extends Abstra ): Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]] = { tour.originActivity match { case Some(originActivity) => - var tripOrigin = originActivity - tour.trips.map { trip => - modes.map { mode => - val skim = getTimeDistanceAndCost( - tripOrigin.getCoord, - trip.activity.getCoord, - originActivity.getEndTime.toInt, - mode, - vehicleTypeId, - vehicleType, - fuelPrice - ) - tripOrigin = trip.activity - mode -> ODSkimmerTimeCostTransfer(skim.time / 3600, skim.cost, 0, 0) - }.toMap - } + Seq(getSkimInfo(originActivity, tour.trips.head.activity, modes, vehicleTypeId, vehicleType, fuelPrice)) ++ + tour.trips + .sliding(2) + .map { case Seq(trip1, trip2) => + getSkimInfo(trip1.activity, trip2.activity, modes, vehicleTypeId, vehicleType, fuelPrice) + } + .toSeq + case _ => Seq[Map[BeamMode, ODSkimmerTimeCostTransfer]]() } } + def getSkimInfo( + activity1: Activity, + activity2: Activity, + modes: Iterable[BeamMode], + vehicleTypeId: Id[BeamVehicleType], + vehicleType: BeamVehicleType, + fuelPrice: Double + ): Map[BeamMode, ODSkimmerTimeCostTransfer] = { + modes.map { mode => + val skim = getTimeDistanceAndCost( + activity1.getCoord, + activity2.getCoord, + activity1.getEndTime.toInt, + mode, + vehicleTypeId, + vehicleType, + fuelPrice + ) + mode -> ODSkimmerTimeCostTransfer(skim.generalizedTime / 3600.0, skim.cost, 0, 0) + }.toMap + } + def getTimeDistanceAndCost( originUTM: Location, destinationUTM: Location, From 8c98c0c245af3bd53a436784348c1122cd014f88 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Fri, 3 Jun 2022 16:10:28 -0700 Subject: [PATCH 21/40] limit trip mode alternatives based on chosen tour mode, bookkeep in PersonData --- .../beam/agentsim/agents/PersonAgent.scala | 8 +- .../agents/modalbehaviors/ChoosesMode.scala | 74 +++++++++++-------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index ab4a1dfe193..92d56879171 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -1002,12 +1002,16 @@ class PersonAgent( ) val currentCoord = beamServices.geo.wgs2Utm(basePersonData.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc - val nextCoord = nextActivity(basePersonData).get.getCoord + val nextAct = nextActivity(basePersonData).get + val nextCoord = nextAct.getCoord // Have to give up my mode as well, perhaps there's no option left for driving. - _experiencedBeamPlan.putStrategy(currentTour(basePersonData), TripModeChoiceStrategy(mode = None)) + _experiencedBeamPlan.putStrategy(nextAct, TripModeChoiceStrategy(mode = None)) + val updatedTourMode: Option[BeamTourMode] = if (isFirstOrLastTripWithinTour(nextAct)) { None } + else { basePersonData.currentTourMode } goto(ChoosingMode) using ChoosesModeData( basePersonData.copy( currentTripMode = None, + currentTourMode = updatedTourMode, currentTourPersonalVehicle = None, numberOfReplanningAttempts = basePersonData.numberOfReplanningAttempts + 1 ), diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 65716279dfb..305108fca31 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -141,11 +141,6 @@ trait ChoosesMode { val currentTourStrategy = _experiencedBeamPlan.getTourStrategy[TourModeChoiceStrategy](nextAct) val currentTripMode = _experiencedBeamPlan.getTripStrategy[TripModeChoiceStrategy](nextAct).mode - val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( - choosesModeData.personData.currentTripMode, - choosesModeData.personData, - availableModes - ) nextStateData match { // If I am already on a tour in a vehicle, only that vehicle is available to me case data: ChoosesModeData @@ -160,7 +155,7 @@ trait ChoosesMode { getCurrentTriggerIdOrGenerate ) // Create teleportation vehicle if we are told to use teleportation - case data: ChoosesModeData if correctedCurrentTripMode.exists(_.isHovTeleportation) => + case data: ChoosesModeData if choosesModeData.personData.currentTripMode.exists(_.isHovTeleportation) => val teleportationVehicle = createSharedTeleportationVehicle(data.currentLocation) val vehicles = Vector(ActualVehicle(teleportationVehicle)) self ! MobilityStatusResponse(vehicles, getCurrentTriggerIdOrGenerate) @@ -238,18 +233,12 @@ trait ChoosesMode { val availableModes: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) val personData = choosesModeData.personData val nextAct = nextActivity(personData).get - // Make sure the current mode is allowable - val correctedCurrentTripMode = correctCurrentTripModeAccordingToRules( - personData.currentTripMode, - personData, - availableModes - ) val bodyStreetVehicle = createBodyStreetVehicle(currentPersonLocation) val departTime = _currentTick.get var availablePersonalStreetVehicles = - correctedCurrentTripMode match { + personData.currentTripMode match { case None | Some(CAR | BIKE) => // In these cases, a personal vehicle will be involved, but filter out teleportation vehicles newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedTeleportationVehicle(v.id)) @@ -274,6 +263,7 @@ trait ChoosesMode { availablePersonalVehicleModes.flatMap(x => BeamTourMode.enabledModes.get(x)).flatten val modesToQuery = (availablePersonalVehicleModes ++ BeamMode.nonPersonalVehicleModes ++ availableFirstAndLastLegModes).distinct + .intersect(availableModes) val dummyVehicleType = beamScenario.vehicleTypes(dummyRHVehicle.vehicleTypeId) val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) val tourModeCosts = beamServices.skims.od_skimmer.getTourModeCosts( @@ -288,16 +278,35 @@ trait ChoosesMode { val firstAndLastTripModeToTourModeOption = BeamTourMode.values .map(tourMode => tourMode -> tourMode.allowedBeamModesForFirstAndLastLeg.intersect(modesToQuery)) .toMap - val done = tourModeChoiceCalculator( + tourModeChoiceCalculator( tourModeCosts, modeChoiceCalculator.modeChoiceLogit, modeToTourMode, Some(firstAndLastTripModeToTourModeOption) ) - println(done) - done } + _experiencedBeamPlan.putStrategy( + _experiencedBeamPlan.getTourContaining(nextAct), + TourModeChoiceStrategy(chosenCurrentTourMode) + ) + + val availableModesGivenTourMode = availableModes.intersect(chosenCurrentTourMode match { + case Some(WALK_BASED) => + val enabledModes = availablePersonalStreetVehicles + .flatMap(veh => + if (veh.vehicle.isSharedVehicle) { BeamTourMode.enabledModes.get(veh.streetVehicle.mode) } + else None + ) + .flatten + val walkBasedModes = + if (isFirstOrLastTripWithinTour(nextAct)) WALK_BASED.allowedBeamModesForFirstAndLastLeg + else WALK_BASED.allowedBeamModes + walkBasedModes ++ enabledModes + case Some(tourMode) => tourMode.allowedBeamModes + case None => BeamMode.allModes + }) + def makeRequestWith( withTransit: Boolean, vehicles: Vector[StreetVehicle], @@ -366,13 +375,13 @@ trait ChoosesMode { } } - val hasRideHail = availableModes.contains(RIDE_HAIL) + val hasRideHail = availableModesGivenTourMode.contains(RIDE_HAIL) var responsePlaceholders = ChoosesModeResponsePlaceholders() var requestId: Option[Int] = None // Form and send requests - correctedCurrentTripMode match { + personData.currentTripMode match { case None => if (hasRideHail) { responsePlaceholders = makeResponsePlaceholders( @@ -389,7 +398,7 @@ trait ChoosesMode { requestId = None } makeRequestWith( - withTransit = availableModes.exists(_.isTransit), + withTransit = availableModesGivenTourMode.exists(_.isTransit), newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle, possibleEgressVehicles = dummySharedVehicles ) @@ -504,7 +513,8 @@ trait ChoosesMode { logDebug(m.toString) } val newPersonData = choosesModeData.copy( - personData = personData.copy(currentTripMode = correctedCurrentTripMode), + personData = personData + .copy(currentTripMode = choosesModeData.personData.currentTripMode, currentTourMode = chosenCurrentTourMode), availablePersonalStreetVehicles = availablePersonalStreetVehicles, allAvailableStreetVehicles = newlyAvailableBeamVehicles, routingResponse = responsePlaceholders.routingResponse, @@ -1458,18 +1468,14 @@ trait ChoosesMode { ).getOrElse("").toString val nextAct = nextActivity(data.personData).get - val currentTour = _experiencedBeamPlan.getTourContaining(nextAct) - val currentTrip = _experiencedBeamPlan.getTripContaining(nextAct) + val tripStrategy = TripModeChoiceStrategy(Some(chosenTrip.tripClassifier)) - val currentTourStrategy = tripStrategy.tourStrategy(_experiencedBeamPlan, currentActivity(data.personData), nextAct) - _experiencedBeamPlan.putStrategy(currentTrip, tripStrategy) - _experiencedBeamPlan.putStrategy(currentTour, currentTourStrategy) // TODO: This doesn't need to be used anymore val modeChoiceEvent = new ModeChoiceEvent( tick, id, chosenTrip.tripClassifier.value, - currentTourStrategy.mode.map(_.value).getOrElse(""), + tripStrategy.mode.map(_.value).getOrElse(""), data.expectedMaxUtilityOfLatestChoice.getOrElse[Double](Double.NaN), _experiencedBeamPlan.activities(data.personData.currentActivityIndex).getLinkId.toString, data.availableAlternatives.get, @@ -1527,16 +1533,20 @@ trait ChoosesMode { ) ) ) + val currentTourPersonalVehicle = + if (isCurrentPersonalVehicleVoided) + vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id) + else + data.personData.currentTourPersonalVehicle + .orElse(vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id)) + val updatedTourStrategy = + TourModeChoiceStrategy(_experiencedBeamPlan.getTourStrategy(nextAct), currentTourPersonalVehicle) + _experiencedBeamPlan.putStrategy(_experiencedBeamPlan.getTourContaining(nextAct), updatedTourStrategy) goto(WaitingForDeparture) using data.personData.copy( currentTrip = Some(chosenTrip), restOfCurrentTrip = chosenTrip.legs.toList, currentTripMode = Some(chosenTrip.tripClassifier), - currentTourPersonalVehicle = - if (isCurrentPersonalVehicleVoided) - vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id) - else - data.personData.currentTourPersonalVehicle - .orElse(vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id)), + currentTourPersonalVehicle = currentTourPersonalVehicle, failedTrips = data.personData.failedTrips ++ data.personData.currentTrip ) } From d92ff28b8aa23d11f4815d95d783a0750f6becd3 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Fri, 3 Jun 2022 16:51:54 -0700 Subject: [PATCH 22/40] clarify mobilityStatusInquiry logic --- .../agents/modalbehaviors/ChoosesMode.scala | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 305108fca31..5c7a8ec09ce 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -179,7 +179,13 @@ trait ChoosesMode { ) pipeTo self // Drive/Bike transit tours count as WALK_BASED tours, // but they only have access to their vehicles on first/last leg - case Some(WALK_BASED) | None if isFirstTripWithinTour(nextAct) || isLastTripWithinTour(nextAct) => + case Some(WALK_BASED) if isFirstTripWithinTour(nextAct) || isLastTripWithinTour(nextAct) => + requestAvailableVehicles( + vehicleFleets, + data.currentLocation, + currentActivity(data.personData) + ) pipeTo self + case None => requestAvailableVehicles( vehicleFleets, data.currentLocation, @@ -1311,6 +1317,7 @@ trait ChoosesMode { val correctedTripMode = correctCurrentTripModeAccordingToRules(None, personData, availableModesForTrips) if (correctedTripMode != personData.currentTripMode) { //give another chance to make a choice without predefined mode + //TODO: Do we need to do anything with tour mode here? gotoChoosingModeWithoutPredefinedMode(choosesModeData) } else { val expensiveWalkTrip = createExpensiveWalkTrip(currentPersonLocation, nextAct, routingResponse) @@ -1469,13 +1476,13 @@ trait ChoosesMode { val nextAct = nextActivity(data.personData).get - val tripStrategy = TripModeChoiceStrategy(Some(chosenTrip.tripClassifier)) + val tourMode = data.personData.currentTourMode val modeChoiceEvent = new ModeChoiceEvent( tick, id, chosenTrip.tripClassifier.value, - tripStrategy.mode.map(_.value).getOrElse(""), + tourMode.map(_.value).getOrElse(""), data.expectedMaxUtilityOfLatestChoice.getOrElse[Double](Double.NaN), _experiencedBeamPlan.activities(data.personData.currentActivityIndex).getLinkId.toString, data.availableAlternatives.get, From fee87ad62004c68e8b866086f73908383e64ac56 Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Fri, 3 Jun 2022 17:32:34 -0700 Subject: [PATCH 23/40] Fix mobility status request --- .../beam/agentsim/agents/PersonAgent.scala | 2 +- .../agents/modalbehaviors/ChoosesMode.scala | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 92d56879171..08d22a4188d 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -619,7 +619,7 @@ class PersonAgent( // If we have the currentTourPersonalVehicle then we should use it // use the mode of the next leg as the new trip mode. currentTripMode = modeOfNextLeg, - currentTourMode = None, // currentTourModeChoiceStrategy.tourMode, + currentTourMode = None, // currentTourModeChoiceStrategy.tourMode, NOTE: Not sure why this is needed... numberOfReplanningAttempts = 0, failedTrips = IndexedSeq.empty, enrouteData = EnrouteData() diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 5c7a8ec09ce..b6297211ca9 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -162,36 +162,27 @@ trait ChoosesMode { // Only need to get available street vehicles if our mode requires such a vehicle case data: ChoosesModeData => implicit val executionContext: ExecutionContext = context.system.dispatcher - currentTourStrategy.tourMode match { - case Some(CAR_BASED) => + currentTripMode match { + case Some(CAR | DRIVE_TRANSIT) => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData), Some(VehicleCategory.Car) ) pipeTo self - case Some(BIKE_BASED) => + case Some(BIKE | BIKE_TRANSIT) => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData), Some(VehicleCategory.Bike) ) pipeTo self - // Drive/Bike transit tours count as WALK_BASED tours, - // but they only have access to their vehicles on first/last leg - case Some(WALK_BASED) if isFirstTripWithinTour(nextAct) || isLastTripWithinTour(nextAct) => - requestAvailableVehicles( - vehicleFleets, - data.currentLocation, - currentActivity(data.personData) - ) pipeTo self - case None => + case _ => requestAvailableVehicles( vehicleFleets, data.currentLocation, currentActivity(data.personData) ) pipeTo self - case _ => self ! MobilityStatusResponse(Vector(), getCurrentTriggerIdOrGenerate) } // Otherwise, send empty list to self From 57edf101678ad34093570284a539647e5dc3dd9b Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Mon, 6 Jun 2022 09:05:25 -0700 Subject: [PATCH 24/40] Bugfix, limit modes in mode choice, rename tour mode -> trip mode in events --- .../beam/agentsim/agents/PersonAgent.scala | 2 +- .../agents/modalbehaviors/ChoosesMode.scala | 54 ++++++++++++------- .../agentsim/events/PathTraversalEvent.scala | 16 +++--- .../agentsim/events/TeleportationEvent.scala | 6 +-- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 08d22a4188d..3335b8cb76a 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -663,7 +663,7 @@ class PersonAgent( startY = currentTrip.legs.head.beamLeg.travelPath.startPoint.loc.getY, endX = currentTrip.legs.last.beamLeg.travelPath.endPoint.loc.getX, endY = currentTrip.legs.last.beamLeg.travelPath.endPoint.loc.getY, - currentTourMode = maybeCurrentTripMode.map(_.value) + currentTripMode = data.currentTripMode.map(_.value) ) eventsManager.processEvent(teleportationEvent) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index b6297211ca9..3bde6ba7a49 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -288,21 +288,12 @@ trait ChoosesMode { TourModeChoiceStrategy(chosenCurrentTourMode) ) - val availableModesGivenTourMode = availableModes.intersect(chosenCurrentTourMode match { - case Some(WALK_BASED) => - val enabledModes = availablePersonalStreetVehicles - .flatMap(veh => - if (veh.vehicle.isSharedVehicle) { BeamTourMode.enabledModes.get(veh.streetVehicle.mode) } - else None - ) - .flatten - val walkBasedModes = - if (isFirstOrLastTripWithinTour(nextAct)) WALK_BASED.allowedBeamModesForFirstAndLastLeg - else WALK_BASED.allowedBeamModes - walkBasedModes ++ enabledModes - case Some(tourMode) => tourMode.allowedBeamModes - case None => BeamMode.allModes - }) + val availableModesGivenTourMode = getAvailableModesGivenTourMode( + availableModes, + availablePersonalStreetVehicles, + chosenCurrentTourMode, + isFirstOrLastTripWithinTour(nextAct) + ) def makeRequestWith( withTransit: Boolean, @@ -1116,6 +1107,29 @@ trait ChoosesMode { } } + def getAvailableModesGivenTourMode( + availableModes: Seq[BeamMode], + availablePersonalStreetVehicles: Vector[VehicleOrToken], + currentTourMode: Option[BeamTourMode], + isFirstOrLastTrip: Boolean = false + ): Seq[BeamMode] = { + availableModes.intersect(currentTourMode match { + case Some(WALK_BASED) => + val enabledModes = availablePersonalStreetVehicles + .flatMap(veh => + if (veh.vehicle.isSharedVehicle) { BeamTourMode.enabledModes.get(veh.streetVehicle.mode) } + else None + ) + .flatten + val walkBasedModes = + if (isFirstOrLastTrip) WALK_BASED.allowedBeamModesForFirstAndLastLeg + else WALK_BASED.allowedBeamModes + walkBasedModes ++ enabledModes + case Some(tourMode) => tourMode.allowedBeamModes + case None => BeamMode.allModes + }) + } + def mustBeDrivenHome(vehicle: VehicleOrToken): Boolean = { vehicle match { case ActualVehicle(beamVehicle) => @@ -1222,8 +1236,12 @@ trait ChoosesMode { case _ => } - val availableModesForTrips: Seq[BeamMode] = - availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes) + val availableModesForTrips = getAvailableModesGivenTourMode( + availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes), + choosesModeData.availablePersonalStreetVehicles, + choosesModeData.personData.currentTourMode, + isFirstOrLastTripWithinTour(nextAct) + ) val filteredItinerariesForChoice = (choosesModeData.personData.currentTripMode match { case Some(mode) if mode == DRIVE_TRANSIT || mode == BIKE_TRANSIT => @@ -1538,7 +1556,7 @@ trait ChoosesMode { data.personData.currentTourPersonalVehicle .orElse(vehiclesUsed.headOption.filter(mustBeDrivenHome).map(_.id)) val updatedTourStrategy = - TourModeChoiceStrategy(_experiencedBeamPlan.getTourStrategy(nextAct), currentTourPersonalVehicle) + TourModeChoiceStrategy(data.personData.currentTourMode, currentTourPersonalVehicle) _experiencedBeamPlan.putStrategy(_experiencedBeamPlan.getTourContaining(nextAct), updatedTourStrategy) goto(WaitingForDeparture) using data.personData.copy( currentTrip = Some(chosenTrip), diff --git a/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala b/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala index 7969d7018c9..851833f2cf6 100644 --- a/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala +++ b/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala @@ -41,7 +41,7 @@ case class PathTraversalEvent( amountPaid: Double, fromStopIndex: Option[Int], toStopIndex: Option[Int], - currentTourMode: Option[String], + currentTripMode: Option[String], /*, linkIdsToLaneOptions: IndexedSeq[(Int, Option[Int])], linkIdsToSpeedOptions: IndexedSeq[(Int, Option[Double])], @@ -96,7 +96,7 @@ case class PathTraversalEvent( attr.put(ATTRIBUTE_TOLL_PAID, amountPaid.toString) attr.put(ATTRIBUTE_FROM_STOP_INDEX, fromStopIndex.map(_.toString).getOrElse("")) attr.put(ATTRIBUTE_TO_STOP_INDEX, toStopIndex.map(_.toString).getOrElse("")) - attr.put(ATTRIBUTE_CURRENT_TOUR_MODE, currentTourMode.getOrElse("")) + attr.put(ATTRIBUTE_CURRENT_TRIP_MODE, currentTripMode.getOrElse("")) /* attr.put(ATTRIBUTE_LINKID_WITH_LANE_MAP, linkIdsToLaneOptions.map{case ((linkId, laneOption)) => s"$linkId:${laneOption.getOrElse(0)}"}.mkString(",")) attr.put(ATTRIBUTE_LINKID_WITH_SPEED_MAP, linkIdsToSpeedOptions.map{case ((linkId, speedOption)) => s"$linkId:${speedOption.getOrElse(0)}"}.mkString(",")) @@ -123,7 +123,7 @@ object PathTraversalEvent { val ATTRIBUTE_PRIMARY_FUEL: String = "primaryFuel" val ATTRIBUTE_SECONDARY_FUEL: String = "secondaryFuel" val ATTRIBUTE_NUM_PASS: String = "numPassengers" - val ATTRIBUTE_CURRENT_TOUR_MODE: String = "currentTourMode" + val ATTRIBUTE_CURRENT_TRIP_MODE: String = "currentTripMode" val ATTRIBUTE_LINK_IDS: String = "links" val ATTRIBUTE_LINK_TRAVEL_TIME: String = "linkTravelTime" @@ -163,7 +163,7 @@ object PathTraversalEvent { vehicleType: BeamVehicleType, numPass: Int, beamLeg: BeamLeg, - currentTourMode: Option[String], + currentTripMode: Option[String], primaryFuelConsumed: Double, secondaryFuelConsumed: Double, endLegPrimaryFuelLevel: Double, @@ -207,7 +207,7 @@ object PathTraversalEvent { amountPaid = amountPaid, fromStopIndex = beamLeg.travelPath.transitStops.map(_.fromIdx), toStopIndex = beamLeg.travelPath.transitStops.map(_.toIdx), - currentTourMode = currentTourMode, + currentTripMode = currentTripMode, /* linkIdsToLaneOptions = linkIdsToLaneOptions, linkIdsToSpeedOptions = linkIdsToSpeedOptions, @@ -258,8 +258,8 @@ object PathTraversalEvent { attr.get(ATTRIBUTE_FROM_STOP_INDEX).flatMap(Option(_)).flatMap(x => if (x == "") None else Some(x.toInt)) val toStopIndex: Option[Int] = attr.get(ATTRIBUTE_TO_STOP_INDEX).flatMap(Option(_)).flatMap(x => if (x == "") None else Some(x.toInt)) - val currentTourMode: Option[String] = - attr.get(ATTRIBUTE_CURRENT_TOUR_MODE).flatMap(x => if (x == "") None else Some(x)) + val currentTripMode: Option[String] = + attr.get(ATTRIBUTE_CURRENT_TRIP_MODE).flatMap(x => if (x == "") None else Some(x)) /* val linkIdsToLaneOptions = attr(ATTRIBUTE_LINKID_WITH_LANE_MAP).split(",").map(x=>{ val linkIdToLaneSplit = x.split(":") @@ -321,7 +321,7 @@ object PathTraversalEvent { amountPaid, fromStopIndex, toStopIndex, - currentTourMode, + currentTripMode, /*, linkIdsToLaneOptions, linkIdsToSpeedOptions, diff --git a/src/main/scala/beam/agentsim/events/TeleportationEvent.scala b/src/main/scala/beam/agentsim/events/TeleportationEvent.scala index afdc5a2b980..00652e73129 100644 --- a/src/main/scala/beam/agentsim/events/TeleportationEvent.scala +++ b/src/main/scala/beam/agentsim/events/TeleportationEvent.scala @@ -16,7 +16,7 @@ case class TeleportationEvent( startY: Double, endX: Double, endY: Double, - currentTourMode: Option[String] + currentTripMode: Option[String] ) extends Event(time) with ScalaEvent { import TeleportationEvent._ @@ -38,7 +38,7 @@ case class TeleportationEvent( attr.put(ATTRIBUTE_START_COORDINATE_Y, startY.toString) attr.put(ATTRIBUTE_END_COORDINATE_X, endX.toString) attr.put(ATTRIBUTE_END_COORDINATE_Y, endY.toString) - attr.put(ATTRIBUTE_CURRENT_TOUR_MODE, currentTourMode.getOrElse("")) + attr.put(ATTRIBUTE_CURRENT_TRIP_MODE, currentTripMode.getOrElse("")) filledAttrs.set(attr) attr @@ -49,7 +49,7 @@ case class TeleportationEvent( object TeleportationEvent { val EVENT_TYPE: String = "TeleportationEvent" - val ATTRIBUTE_CURRENT_TOUR_MODE: String = "currentTourMode" + val ATTRIBUTE_CURRENT_TRIP_MODE: String = "currentTripMode" val ATTRIBUTE_DEPARTURE_TIME: String = "departureTime" val ATTRIBUTE_PERSON: String = "person" From a11f3e10b9e9ca8a3ef71d09ddd60a045d78066a Mon Sep 17 00:00:00 2001 From: Zach Needell Date: Mon, 6 Jun 2022 16:29:31 -0700 Subject: [PATCH 25/40] fix sign error in utility calcs --- .../agents/choice/mode/TourModeChoiceMultinomialLogit.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala index 9ddd972ab96..757c4cad648 100644 --- a/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/TourModeChoiceMultinomialLogit.scala @@ -42,7 +42,7 @@ class TourModeChoiceMultinomialLogit( val skims = tripModeCosts.getOrElse(beamMode, ODSkimmerTimeCostTransfer()) val timeCost = attributesOfIndividual.getVOT(skims.timeInHours) val monetaryCost = skims.cost - beamMode -> (Map("cost" -> -(timeCost + monetaryCost)) ++ Map( + beamMode -> (Map("cost" -> (timeCost + monetaryCost)) ++ Map( "transfers" -> skims.numTransfers.toDouble )) }.toMap From 6a164713dd587bb75319f83c5b00707a9943ad5d Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 21:23:06 -0700 Subject: [PATCH 26/40] typo correction --- .../agents/modalbehaviors/ChoosesMode.scala | 16 ++++++++++++++++ .../beam/agentsim/agents/planning/BeamPlan.scala | 2 +- src/main/scala/beam/router/Modes.scala | 5 ++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 3bde6ba7a49..2733eb8f4f9 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -1129,6 +1129,22 @@ trait ChoosesMode { case None => BeamMode.allModes }) } +// def getAvailableVehiclesGivenTourMode( +// availableModes: Seq[BeamMode], +// availablePersonalStreetVehicles: Vector[VehicleOrToken], +// currentTourMode: Option[BeamTourMode] +// ): Seq[VehicleOrToken] = { +// availableModes.intersect(currentTourMode match { +// case Some(WALK_BASED) => +// availablePersonalStreetVehicles.map(veh => +// if (veh.vehicle.isSharedVehicle) {veh} +// else None) +// availablePersonalStreetVehicles +// case Some(tourMode) => availablePersonalStreetVehicles +// case None => availablePersonalStreetVehicles +// }) +// } + def mustBeDrivenHome(vehicle: VehicleOrToken): Boolean = { vehicle match { diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 12372047883..f68625b9685 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -248,7 +248,7 @@ class BeamPlan extends Plan { tour.trips.foreach(trip => trip.leg match { case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) - case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) => + case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED)=> tourMode = Some(BIKE_BASED) case Some(_) => tourMode = Some(WALK_BASED) case _ => diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 8c825366fb3..6f0b840be98 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -260,6 +260,9 @@ object TourModes { ) extends StringEnumEntry { import BeamTourMode._ +// def allowedBeamModes(vehicles: Vector[VehicleOrToken]): Seq[BeamMode] = { +// +// } def isVehicleBased: Boolean = this match { case WALK_BASED => false @@ -309,7 +312,7 @@ object TourModes { Seq[BeamMode](CAR, CAR_HOV2, CAR_HOV3) ) - case object BIKE_BASED extends BeamTourMode("bike_based", Car, Seq[BeamMode](BIKE), Seq[BeamMode](BIKE)) + case object BIKE_BASED extends BeamTourMode("bike_based", Bike, Seq[BeamMode](BIKE), Seq[BeamMode](BIKE)) } } From cb51f2cf033d2a911948b4f3ac410b135513d4b9 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 21:53:03 -0700 Subject: [PATCH 27/40] define the function to check vehicle shared or not --- src/main/scala/beam/router/Modes.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 6f0b840be98..98ce9e4c7cc 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -1,5 +1,6 @@ package beam.router +import beam.agentsim.agents.modalbehaviors.DrivesVehicle.VehicleOrToken import beam.agentsim.agents.vehicles.VehicleCategory._ import com.conveyal.r5.api.util.{LegMode, TransitModes} import com.conveyal.r5.profile.StreetMode @@ -260,9 +261,14 @@ object TourModes { ) extends StringEnumEntry { import BeamTourMode._ -// def allowedBeamModes(vehicles: Vector[VehicleOrToken]): Seq[BeamMode] = { -// -// } + + def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { + val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) + if (sharedVehicles.isEmpty) false + else true + } + + def isVehicleBased: Boolean = this match { case WALK_BASED => false From b8d35c0b5f027b6ae2a543fbdfb7b99058e8ae1a Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 21:59:09 -0700 Subject: [PATCH 28/40] adding shared deny into shared bike --- .../scala/beam/agentsim/agents/planning/BeamPlan.scala | 10 ++++++++-- src/main/scala/beam/router/Modes.scala | 2 -- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index f68625b9685..266326e70f4 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -1,5 +1,6 @@ package beam.agentsim.agents.planning +import beam.agentsim.agents.modalbehaviors.DrivesVehicle.VehicleOrToken import beam.agentsim.agents.planning.BeamPlan.atHome import java.{lang, util} @@ -241,14 +242,19 @@ class BeamPlan extends Plan { getTripContaining(activities(index)) } - def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { + def getTourModeFromTourLegs(tour: Tour, vehicles: Vector[VehicleOrToken]): Option[BeamTourMode] = { // TODO: Should this just look at the first/last mode of legs? + def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { + val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) + if (sharedVehicles.isEmpty) false + else true + } var tourMode: Option[BeamTourMode] = None if (tour.trips.exists(trip => trip.leg.isDefined)) { tour.trips.foreach(trip => trip.leg match { case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) - case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED)=> + case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) && !vehicleSharedOrNot(vehicles)=> tourMode = Some(BIKE_BASED) case Some(_) => tourMode = Some(WALK_BASED) case _ => diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 98ce9e4c7cc..00fecae8c00 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -268,8 +268,6 @@ object TourModes { else true } - - def isVehicleBased: Boolean = this match { case WALK_BASED => false case _ => true From 7d3b6ca69305a8b51d66f409436de66aaf0ede5d Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 22:05:46 -0700 Subject: [PATCH 29/40] fix --- .../scala/beam/agentsim/agents/planning/BeamPlan.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 266326e70f4..4954c092e51 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -1,11 +1,12 @@ package beam.agentsim.agents.planning +import beam.agentsim.agents.modalbehaviors.ChoosesMode.ChoosesModeData import beam.agentsim.agents.modalbehaviors.DrivesVehicle.VehicleOrToken import beam.agentsim.agents.planning.BeamPlan.atHome import java.{lang, util} import beam.agentsim.agents.planning.Strategy.{Strategy, TourModeChoiceStrategy, TripModeChoiceStrategy} -import beam.router.Modes.BeamMode +import beam.router.Modes.{BeamMode, isPersonalVehicleMode} import beam.router.TourModes.BeamTourMode import beam.router.TourModes.BeamTourMode._ import org.matsim.api.core.v01.population._ @@ -133,7 +134,7 @@ class BeamPlan extends Plan { if (atHome(activity)) { // TODO: Also trigger this if we return to a location already present in the tour tours = tours :+ nextTour - putStrategy(nextTour, TourModeChoiceStrategy(getTourModeFromTourLegs(nextTour))) + putStrategy(nextTour, TourModeChoiceStrategy(getTourModeFromTourLegs(nextTour, isPersonalVehicleMode()))) nextTour = new Tour(originActivity = Some(activity)) } case leg: Leg => @@ -242,7 +243,7 @@ class BeamPlan extends Plan { getTripContaining(activities(index)) } - def getTourModeFromTourLegs(tour: Tour, vehicles: Vector[VehicleOrToken]): Option[BeamTourMode] = { + def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { // TODO: Should this just look at the first/last mode of legs? def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) @@ -254,7 +255,7 @@ class BeamPlan extends Plan { tour.trips.foreach(trip => trip.leg match { case Some(leg) if leg.getMode.equalsIgnoreCase("car") => tourMode = Some(CAR_BASED) - case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED) && !vehicleSharedOrNot(vehicles)=> + case Some(leg) if leg.getMode.equalsIgnoreCase("bike") && !tourMode.contains(CAR_BASED)=> tourMode = Some(BIKE_BASED) case Some(_) => tourMode = Some(WALK_BASED) case _ => From c045f26b4a68d8a6f78f89deb86a28d4b1caf1d2 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 22:18:21 -0700 Subject: [PATCH 30/40] checks whether the vehicles are shared or not before returning the allowed beam modes, having BIKE_BASED not return BIKE if the only bikes available are shared bikes. --- .../agentsim/agents/modalbehaviors/ChoosesMode.scala | 3 +++ .../beam/agentsim/agents/planning/BeamPlan.scala | 12 ++++++------ src/main/scala/beam/router/Modes.scala | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 2733eb8f4f9..c89f3026431 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -1125,6 +1125,9 @@ trait ChoosesMode { if (isFirstOrLastTrip) WALK_BASED.allowedBeamModesForFirstAndLastLeg else WALK_BASED.allowedBeamModes walkBasedModes ++ enabledModes + case Some(BIKE_BASED) => + if (BIKE_BASED.vehicleSharedOrNot(availablePersonalStreetVehicles)) {Seq[BeamMode]()} + else BIKE_BASED.allowedBeamModes case Some(tourMode) => tourMode.allowedBeamModes case None => BeamMode.allModes }) diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index 4954c092e51..b6c73688d02 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -134,7 +134,7 @@ class BeamPlan extends Plan { if (atHome(activity)) { // TODO: Also trigger this if we return to a location already present in the tour tours = tours :+ nextTour - putStrategy(nextTour, TourModeChoiceStrategy(getTourModeFromTourLegs(nextTour, isPersonalVehicleMode()))) + putStrategy(nextTour, TourModeChoiceStrategy(getTourModeFromTourLegs(nextTour))) nextTour = new Tour(originActivity = Some(activity)) } case leg: Leg => @@ -245,11 +245,11 @@ class BeamPlan extends Plan { def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { // TODO: Should this just look at the first/last mode of legs? - def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { - val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) - if (sharedVehicles.isEmpty) false - else true - } +// def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { +// val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) +// if (sharedVehicles.isEmpty) false +// else true +// } var tourMode: Option[BeamTourMode] = None if (tour.trips.exists(trip => trip.leg.isDefined)) { tour.trips.foreach(trip => diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 00fecae8c00..42975cb37bb 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -317,6 +317,7 @@ object TourModes { ) case object BIKE_BASED extends BeamTourMode("bike_based", Bike, Seq[BeamMode](BIKE), Seq[BeamMode](BIKE)) + } } From f2f1d7892aff3c0ee2d532ca3e7e815a16783119 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Tue, 23 Aug 2022 22:45:40 -0700 Subject: [PATCH 31/40] fix bugs --- build.gradle | 30 +++++++++---------- .../MeansOfTransportationTableReader.scala | 2 +- .../utils/data/synthpop/HouseholdReader.scala | 4 +-- .../data/synthpop/WorkForceSampler.scala | 2 +- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 248bf1880b3..d948c6baad7 100755 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { gradlePluginPortal() } dependencies { - classpath group: 'kr.motd.gradle', name: 'sphinx-gradle-plugin', version: '1.0.3.Final' + classpath group: 'kr.motd.gradle', name: 'sphinx-gradle-plugin', version: '2.10.1' classpath "jp.classmethod.aws.reboot:gradle-aws-plugin-reboot:0.45" classpath "com.github.viswaramamoorthy:gradle-util-plugins:0.1.0-RELEASE" } @@ -39,7 +39,7 @@ apply plugin: 'ManifestClasspath' apply plugin: 'scalafmt' group = 'beam' -version = '0.8.6' +version = '0.8.6.13' description = """""" @@ -96,7 +96,6 @@ allprojects { // maven { url "http://maven.icm.edu.pl/artifactory/repo/" } // maven { url "https://maven.geotoolkit.org/" } maven { url "https://repository.jboss.org/nexus/content/repositories/thirdparty-releases" } - maven { url "https://central.maven.org/maven2" } maven { url "https://repo.maven.apache.org/maven2" } maven { url "https://repo.matsim.org/repository/matsim" } // Used for graphql-java and matsim.contrib.decongestion specific versions - @@ -110,10 +109,6 @@ allprojects { maven { url "https://people.apache.org/repo/m1-ibiblio-rsync-repository/org.apache.axis2/" } maven { url "https://maven.geo-solutions.it" } mavenLocal() - maven { - url "http://nexus.onebusaway.org/content/groups/public/" - allowInsecureProtocol = true - } maven { url "https://jitpack.io" } } } @@ -126,8 +121,11 @@ configurations { configurations.all { resolutionStrategy { -// we need this in order to get Elki library working fine. + // we need this in order to get Elki library working fine. + force 'net.jafama:jafama:2.3.2' + // we need this in order to get Elki library working fine. force 'it.unimi.dsi:fastutil:8.5.4' + force 'org.apache.logging.log4j:log4j-core:2.15.0' force 'org.apache.logging.log4j:log4j-api:2.15.0' } @@ -136,7 +134,7 @@ configurations.all { dependencies { - implementation(group: 'com.github.LBNL-UCB-STI', name: 'beam-utilities', version: 'v0.2.17') { + implementation(group: 'com.github.LBNL-UCB-STI', name: 'beam-utilities', version: 'v0.2.19') { exclude group: 'com.github.LBNL-UCB-STI', module: 'r5' exclude group: 'org.matsim', module: 'matsim' } @@ -212,7 +210,7 @@ dependencies { testImplementation group: 'junit', name: 'junit', version: '4.8' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '2.27.0' - testImplementation group: "org.mockito", name: "mockito-core", version: "2.+" + testImplementation group: "org.mockito", name: "mockito-core", version: "2.28.2" jmhImplementation group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.23' jmhImplementation group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.23' @@ -222,11 +220,11 @@ dependencies { ///////////////////////////////// // CORE Scala // - implementation "org.scala-lang:scala-library:2.12.13" + implementation "org.scala-lang:scala-library:2.12.16" implementation group: 'org.scala-lang.modules', name: "scala-xml_${scalaBinaryVersion}", version: '1.0.6' // NEEDED FOR USING REPL // - implementation "org.scala-lang:scala-compiler:2.12.13" + implementation "org.scala-lang:scala-compiler:2.12.16" // TEST Scala // testImplementation group: 'org.scalatest', name: "scalatest_${scalaBinaryVersion}", version: '3.2.9' @@ -539,7 +537,7 @@ def jfr = ["-XX:+UnlockCommercialFeatures", "-XX:+FlightRecorder", // On the running machine there should be file /usr/lib/jvm/java-8-oracle/jre/lib/jfr/profile_heap_exception.jfc with content from // https://pastebin.com/N3uuUfPz - it's Java Mission Control with metrics about heap allocation and details about exceptions def jfrWithMem = ["-XX:+UnlockCommercialFeatures", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints", - "-XX:StartFlightRecording=delay=2s,duration=60m,name=mem_ex,filename=recording.jfr,settings=profile_heap_exception", + "-XX:StartFlightRecording=delay=2s,duration=180m,name=mem_ex,filename=recording.jfr,settings=profile_heap_exception", "-XX:+FlightRecorder", "-XX:FlightRecorderOptions=disk=true,maxage=10h,dumponexit=true,loglevel=info"] @@ -692,8 +690,8 @@ task createDockerfile(type: Dockerfile, dependsOn: dockerSyncBuildContext) { task buildImageWithoutTags(type: DockerBuildImage, dependsOn: createDockerfile) {} task buildImage(type: DockerTagImage, dependsOn: buildImageWithoutTags) { - repository = "beammodel/beam" - tag = version + repository = "haitamlaarabi/beam" + tag = 'gemini-develop-overnight-v1' targetImageId buildImageWithoutTags.getImageId() } @@ -722,4 +720,4 @@ jmh { jvmArgs = ['-Djmh.separateClasspathJAR=true', '-Dbeam.home=' + project.projectDir] duplicateClassesStrategy = 'exclude' zip64 = true -} +} \ No newline at end of file diff --git a/src/main/scala/beam/utils/data/ctpp/readers/flow/MeansOfTransportationTableReader.scala b/src/main/scala/beam/utils/data/ctpp/readers/flow/MeansOfTransportationTableReader.scala index 531ebebcf21..ecff3e6af53 100644 --- a/src/main/scala/beam/utils/data/ctpp/readers/flow/MeansOfTransportationTableReader.scala +++ b/src/main/scala/beam/utils/data/ctpp/readers/flow/MeansOfTransportationTableReader.scala @@ -63,7 +63,7 @@ object MeansOfTransportationTableReader { val totalModes = modeToSum.map(_._2).sum println(s"The sum of all modes: $totalModes") modeToSum.foreach { case (mode, sum) => - val pct = (100 * sum.toDouble / totalModes).formatted("%.2f") + val pct = "%.2f".format(100 * sum.toDouble / totalModes) println(s"$mode => $sum, $pct %") } } diff --git a/src/main/scala/beam/utils/data/synthpop/HouseholdReader.scala b/src/main/scala/beam/utils/data/synthpop/HouseholdReader.scala index df36d68e6c2..afea7be9146 100644 --- a/src/main/scala/beam/utils/data/synthpop/HouseholdReader.scala +++ b/src/main/scala/beam/utils/data/synthpop/HouseholdReader.scala @@ -50,10 +50,10 @@ class HouseholdReader(val pathToHouseholdFile: String) extends StrictLogging { val state = State(GenericCsvReader.getIfNotNull(rec, "state")) val countyAsInt = GenericCsvReader.getIfNotNull(rec, "county").toInt // In order to match with Shape file we need to format it. In shape file COUNTYFP attribute consist of 3 digits (possibly zeros) - val county = County(countyAsInt.formatted("%03d")) + val county = County("%03d".format(countyAsInt)) // In order to match with Shape file we need to format it. In shape file TRACTCE attribute consist of 6 digits (possibly zeros) - val tract = GenericCsvReader.getIfNotNull(rec, "tract").toInt.formatted("%06d") + val tract = "%06d".format(GenericCsvReader.getIfNotNull(rec, "tract").toInt) val blockGroupId = GenericCsvReader.getIfNotNull(rec, "block group") val geoId = BlockGroupGeoId(state = state, county = county, tract = tract, blockGroup = blockGroupId) diff --git a/src/main/scala/beam/utils/data/synthpop/WorkForceSampler.scala b/src/main/scala/beam/utils/data/synthpop/WorkForceSampler.scala index 70c8dc98980..85a26473105 100644 --- a/src/main/scala/beam/utils/data/synthpop/WorkForceSampler.scala +++ b/src/main/scala/beam/utils/data/synthpop/WorkForceSampler.scala @@ -42,7 +42,7 @@ class WorkForceSampler(val dbInfo: CTPPDatabaseInfo, val stateCode: String, val workerAgeToStats.foreach { case (ageRng, stat) => val ratio = stat.totalWorkers.toDouble / stat.totalPeople logger.info( - s"$ageRng => Total people: ${stat.totalPeople}, total workers: ${stat.totalWorkers}, ratio: ${ratio.formatted("%.3f")}" + s"$ageRng => Total people: ${stat.totalPeople}, total workers: ${stat.totalWorkers}, ratio: ${"%.3f".format(ratio)}" ) } From 0a7b7403f088ac7fc2676bf2a81da31a87a800bf Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Wed, 24 Aug 2022 11:18:47 -0700 Subject: [PATCH 32/40] testing on the changes --- .../scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index c89f3026431..9262c169b05 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -44,6 +44,7 @@ import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext, Future} import scala.jdk.CollectionConverters.asJavaIterableConverter + /** * BEAM */ From 59041d174fa87ac8df7aef51acccc3d8b60d06a4 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Wed, 24 Aug 2022 11:29:33 -0700 Subject: [PATCH 33/40] only not reaturning BIKE when all are just bikes --- src/main/scala/beam/router/Modes.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 42975cb37bb..8fa03e2fde4 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -263,9 +263,7 @@ object TourModes { import BeamTourMode._ def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { - val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) - if (sharedVehicles.isEmpty) false - else true + vehicles.forall(_.vehicle.isSharedVehicle) } def isVehicleBased: Boolean = this match { From 87ac571810c40785426dddd2214433a58227dd36 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Wed, 24 Aug 2022 16:28:49 -0700 Subject: [PATCH 34/40] creating available vehicles filter --- .../agentsim/agents/modalbehaviors/ChoosesMode.scala | 11 +++++++++-- .../beam/agentsim/agents/planning/BeamPlan.scala | 5 ----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 9262c169b05..bbce0c0e5e0 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -27,7 +27,7 @@ import beam.router.model.{BeamLeg, EmbodiedBeamLeg, EmbodiedBeamTrip} import beam.router.skim.core.ODSkimmer import beam.router.skim.event.ODSkimmerFailedTripEvent import beam.router.skim.readonly.ODSkims -import beam.router.{Modes, RoutingWorker} +import beam.router.{Modes, RoutingWorker, TourModes} import beam.sim.population.AttributesOfIndividual import beam.sim.{BeamServices, Geofence} import beam.utils.logging.pattern.ask @@ -386,6 +386,13 @@ trait ChoosesMode { responsePlaceholders = makeResponsePlaceholders(withRouting = true) requestId = None } + var availableVehicles = newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle + personData.currentTourMode match { + case Some(WALK_BASED) => availableVehicles = availableVehicles, + case Some(WALK_BASED) => availableVehicles = availableVehicles, + case Some(CAR_BASED) => availableVehicles = availableVehicles + } + makeRequestWith( withTransit = availableModesGivenTourMode.exists(_.isTransit), newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle, @@ -1255,7 +1262,7 @@ trait ChoosesMode { ) case _ => } - + // TODO: available vehicles val availableModesForTrips = getAvailableModesGivenTourMode( availableModesForPerson(matsimPlan.getPerson, choosesModeData.excludeModes), choosesModeData.availablePersonalStreetVehicles, diff --git a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala index b6c73688d02..591024838bb 100755 --- a/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala +++ b/src/main/scala/beam/agentsim/agents/planning/BeamPlan.scala @@ -245,11 +245,6 @@ class BeamPlan extends Plan { def getTourModeFromTourLegs(tour: Tour): Option[BeamTourMode] = { // TODO: Should this just look at the first/last mode of legs? -// def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { -// val sharedVehicles = vehicles.filter(_.vehicle.isSharedVehicle) -// if (sharedVehicles.isEmpty) false -// else true -// } var tourMode: Option[BeamTourMode] = None if (tour.trips.exists(trip => trip.leg.isDefined)) { tour.trips.foreach(trip => From 895a822621288636de5f214cf5405eca95ad778e Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Wed, 24 Aug 2022 16:34:55 -0700 Subject: [PATCH 35/40] creating helper for isShared --- .../beam/agentsim/agents/modalbehaviors/ChoosesMode.scala | 5 +++-- .../scala/beam/agentsim/agents/vehicles/BeamVehicle.scala | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index bbce0c0e5e0..9d6df5c2ef4 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -388,8 +388,9 @@ trait ChoosesMode { } var availableVehicles = newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle personData.currentTourMode match { - case Some(WALK_BASED) => availableVehicles = availableVehicles, - case Some(WALK_BASED) => availableVehicles = availableVehicles, + + case Some(BIKE_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedTeleportationVehicle(v.id)).map(_.streetVehicle) + case Some(WALK_BASED) => availableVehicles = availableVehicles case Some(CAR_BASED) => availableVehicles = availableVehicles } diff --git a/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala b/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala index 7ddaedcecf6..3b9f4bcd562 100755 --- a/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala +++ b/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala @@ -546,6 +546,7 @@ object BeamVehicle { val idPrefixSharedTeleportationVehicle = "teleportationSharedVehicle" val idPrefixRideHail = "rideHailVehicle" + val idPrefixSharedVehicle = "sharedVehicle" def isRidehailVehicle(vehicleId: Id[BeamVehicle]): Boolean = { vehicleId.toString.startsWith(idPrefixRideHail) @@ -554,6 +555,9 @@ object BeamVehicle { def isSharedTeleportationVehicle(vehicleId: Id[BeamVehicle]): Boolean = { vehicleId.toString.startsWith(idPrefixSharedTeleportationVehicle) } + def isSharedVehicle(vehicleId: Id[BeamVehicle]): Boolean = { + vehicleId.toString.startsWith(idPrefixSharedVehicle) + } def noSpecialChars(theString: String): String = theString From aa1f7b49e12e5a4ad4492fe5d7d1b6cd0b39dc36 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Wed, 24 Aug 2022 16:36:46 -0700 Subject: [PATCH 36/40] create available vehicle filter --- .../beam/agentsim/agents/modalbehaviors/ChoosesMode.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 9d6df5c2ef4..a29a714e965 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -389,14 +389,14 @@ trait ChoosesMode { var availableVehicles = newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle personData.currentTourMode match { - case Some(BIKE_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedTeleportationVehicle(v.id)).map(_.streetVehicle) + case Some(BIKE_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedVehicle(v.id)).map(_.streetVehicle) :+ bodyStreetVehicle case Some(WALK_BASED) => availableVehicles = availableVehicles - case Some(CAR_BASED) => availableVehicles = availableVehicles + case Some(CAR_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedVehicle(v.id)).map(_.streetVehicle) :+ bodyStreetVehicle } makeRequestWith( withTransit = availableModesGivenTourMode.exists(_.isTransit), - newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle, + availableVehicles, possibleEgressVehicles = dummySharedVehicles ) case Some(WALK) => From d0df8e5937e58d67ef8d9d1d6e227c6fe7c11b1b Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Thu, 25 Aug 2022 14:35:08 -0700 Subject: [PATCH 37/40] addressing peer advice --- .../python/beam_lambda/lambda_function.py | 15 +++----- .../agents/modalbehaviors/ChoosesMode.scala | 36 ++++++------------- .../agents/vehicles/BeamVehicle.scala | 4 --- src/main/scala/beam/router/Modes.scala | 14 ++++++++ 4 files changed, 28 insertions(+), 41 deletions(-) diff --git a/aws/src/main/python/beam_lambda/lambda_function.py b/aws/src/main/python/beam_lambda/lambda_function.py index 16441940e75..ad4c820bcd1 100755 --- a/aws/src/main/python/beam_lambda/lambda_function.py +++ b/aws/src/main/python/beam_lambda/lambda_function.py @@ -169,7 +169,6 @@ fi exit 0; path: /home/ubuntu/check_simulation_result.sh - runcmd: - sudo chmod +x /home/ubuntu/install-and-run-helics-scripts.sh - sudo chmod +x /home/ubuntu/write-cpu-ram-usage.sh @@ -192,7 +191,6 @@ - RESOLVED_COMMIT=$COMMIT - fi - echo "Resolved commit is $RESOLVED_COMMIT" - - 'echo "sudo git fetch"' - sudo git fetch - 'echo "GIT_LFS_SKIP_SMUDGE=1 sudo git checkout $BRANCH $(date)"' @@ -203,7 +201,6 @@ - sudo git lfs pull - echo "sudo git checkout -qf ..." - GIT_LFS_SKIP_SMUDGE=1 sudo git checkout -qf $COMMIT - - production_data_submodules=$(git submodule | awk '{ print $2 }') - for i in $production_data_submodules - do @@ -227,19 +224,17 @@ - esac - done - done - - if [ "$RUN_JUPYTER" = "True" ] - then - echo "Starting Jupyter" - sudo ./gradlew jupyterStart -Puser=root -PjupyterToken=$JUPYTER_TOKEN - fi - + - if [ "$RUN_BEAM" = "True" ] - then - - echo "-------------------Starting Beam Sim----------------------" - echo $(date +%s) > /tmp/.starttime - - rm -rf /home/ubuntu/git/beam/test/input/sf-light/r5/network.dat + - rm -rf /home/ubuntu/git/beam/test/input/sf-light/r5/network.dat - hello_msg=$(printf "Run Started \\n Run Name** $TITLED** \\n Instance ID %s \\n Instance type **%s** \\n Host name **%s** \\n Web browser ** http://%s:8000 ** \\n Region $REGION \\n Batch $UID \\n Branch **$BRANCH** \\n Commit $COMMIT" $(ec2metadata --instance-id) $(ec2metadata --instance-type) $(ec2metadata --public-hostname) $(ec2metadata --public-hostname)) - start_json=$(printf "{ \\"command\\":\\"add\\", @@ -274,7 +269,6 @@ - crontab /tmp/cron_jobs - crontab -l - echo "notification scheduled..." - - 'echo "gradlew assemble: $(date)"' - ./gradlew assemble - 'echo "sudo chown -R ubuntu:ubuntu ."' @@ -286,7 +280,6 @@ - export GOOGLE_API_KEY="$GOOGLE_API_KEY" - echo $MAXRAM - /tmp/slack.sh "$hello_msg" - - s3p="" - for cf in $CONFIG - do @@ -299,7 +292,7 @@ - do - export $metric=$count - done < RunHealthAnalysis.txt - + - curl -H "Authorization:Bearer $SLACK_TOKEN" -F file=@RunHealthAnalysis.txt -F initial_comment="Beam Health Analysis" -F channels="$SLACK_CHANNEL" "https://slack.com/api/files.upload" - s3glip="" - if [ "$S3_PUBLISH" = "True" ] @@ -956,4 +949,4 @@ def lambda_handler(event, context): if command_id in instance_operations: return instance_handler(event) - return "Operation {command} not supported, please specify one of the supported operations (deploy | start | stop | terminate | log). ".format(command=command_id) + return "Operation {command} not supported, please specify one of the supported operations (deploy | start | stop | terminate | log). ".format(command=command_id) \ No newline at end of file diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 5d7424b817f..fae3bdfa899 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -386,17 +386,18 @@ trait ChoosesMode { responsePlaceholders = makeResponsePlaceholders(withRouting = true) requestId = None } - var availableVehicles = newlyAvailableBeamVehicles.map(_.streetVehicle) :+ bodyStreetVehicle - personData.currentTourMode match { - case Some(BIKE_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedVehicle(v.id)).map(_.streetVehicle) :+ bodyStreetVehicle - case Some(WALK_BASED) => availableVehicles = availableVehicles - case Some(CAR_BASED) => availableVehicles = newlyAvailableBeamVehicles.filterNot(v => BeamVehicle.isSharedVehicle(v.id)).map(_.streetVehicle) :+ bodyStreetVehicle - } + val availableStreetVehiclesGivenTourMode = newlyAvailableBeamVehicles.map { vehicleOrToken => + chosenCurrentTourMode match { + case Some(BIKE_BASED) if !vehicleOrToken.vehicle.isSharedVehicle => vehicleOrToken.streetVehicle + case Some(CAR_BASED) if !vehicleOrToken.vehicle.isSharedVehicle => vehicleOrToken.streetVehicle + case _ => vehicleOrToken.streetVehicle + } + } :+ bodyStreetVehicle makeRequestWith( withTransit = availableModesGivenTourMode.exists(_.isTransit), - availableVehicles, + availableStreetVehiclesGivenTourMode, possibleEgressVehicles = dummySharedVehicles ) case Some(WALK) => @@ -1146,28 +1147,11 @@ trait ChoosesMode { if (isFirstOrLastTrip) WALK_BASED.allowedBeamModesForFirstAndLastLeg else WALK_BASED.allowedBeamModes walkBasedModes ++ enabledModes - case Some(BIKE_BASED) => - if (BIKE_BASED.vehicleSharedOrNot(availablePersonalStreetVehicles)) {Seq[BeamMode]()} - else BIKE_BASED.allowedBeamModes - case Some(tourMode) => tourMode.allowedBeamModes + + case Some(tourMode) => tourMode.allowedBeamModesGivenAvailableVehicles(availablePersonalStreetVehicles, isFirstOrLastTrip) case None => BeamMode.allModes }) } -// def getAvailableVehiclesGivenTourMode( -// availableModes: Seq[BeamMode], -// availablePersonalStreetVehicles: Vector[VehicleOrToken], -// currentTourMode: Option[BeamTourMode] -// ): Seq[VehicleOrToken] = { -// availableModes.intersect(currentTourMode match { -// case Some(WALK_BASED) => -// availablePersonalStreetVehicles.map(veh => -// if (veh.vehicle.isSharedVehicle) {veh} -// else None) -// availablePersonalStreetVehicles -// case Some(tourMode) => availablePersonalStreetVehicles -// case None => availablePersonalStreetVehicles -// }) -// } def mustBeDrivenHome(vehicle: VehicleOrToken): Boolean = { diff --git a/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala b/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala index 422319b9909..a35186cce44 100755 --- a/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala +++ b/src/main/scala/beam/agentsim/agents/vehicles/BeamVehicle.scala @@ -536,7 +536,6 @@ object BeamVehicle { val idPrefixSharedTeleportationVehicle = "teleportationSharedVehicle" val idPrefixRideHail = "rideHailVehicle" - val idPrefixSharedVehicle = "sharedVehicle" def isRidehailVehicle(vehicleId: Id[BeamVehicle]): Boolean = { vehicleId.toString.startsWith(idPrefixRideHail) @@ -545,9 +544,6 @@ object BeamVehicle { def isSharedTeleportationVehicle(vehicleId: Id[BeamVehicle]): Boolean = { vehicleId.toString.startsWith(idPrefixSharedTeleportationVehicle) } - def isSharedVehicle(vehicleId: Id[BeamVehicle]): Boolean = { - vehicleId.toString.startsWith(idPrefixSharedVehicle) - } def noSpecialChars(theString: String): String = theString diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 8fa03e2fde4..40bc0d4c744 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -266,6 +266,20 @@ object TourModes { vehicles.forall(_.vehicle.isSharedVehicle) } + def allowedBeamModesGivenAvailableVehicles( + vehicles: Vector[VehicleOrToken], + firstOrLastLeg: Boolean + ): Seq[BeamMode] = { + val relevantModes = if (firstOrLastLeg) { allowedBeamModesForFirstAndLastLeg } + else allowedBeamModes + if ( + vehicles.exists(vehOrToken => + !vehOrToken.vehicle.isSharedVehicle && vehOrToken.streetVehicle.mode.in(relevantModes) + ) + ) { relevantModes } + else { Seq.empty[BeamMode] } + } + def isVehicleBased: Boolean = this match { case WALK_BASED => false case _ => true From e2540a66f630be302dac7695cfafa46db1fb6a17 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Thu, 25 Aug 2022 14:46:26 -0700 Subject: [PATCH 38/40] fix compilation --- production/sfbay | 2 +- .../beam/agentsim/agents/modalbehaviors/ChoosesMode.scala | 1 - src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala | 7 +++++-- .../scala/beam/agentsim/agents/TeleportationSpec.scala | 4 ++-- .../analysis/cartraveltime/StudyAreaTripFilterTest.scala | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/production/sfbay b/production/sfbay index 786b20833d3..87ccd9b0303 160000 --- a/production/sfbay +++ b/production/sfbay @@ -1 +1 @@ -Subproject commit 786b20833d34d1dfb57cff531717c31e7ea43e8c +Subproject commit 87ccd9b03035c947a94a8e417f80b496c853238d diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index fae3bdfa899..d83c4a90c6a 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -1147,7 +1147,6 @@ trait ChoosesMode { if (isFirstOrLastTrip) WALK_BASED.allowedBeamModesForFirstAndLastLeg else WALK_BASED.allowedBeamModes walkBasedModes ++ enabledModes - case Some(tourMode) => tourMode.allowedBeamModesGivenAvailableVehicles(availablePersonalStreetVehicles, isFirstOrLastTrip) case None => BeamMode.allModes }) diff --git a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala index 34e4004a6e3..a6546f1e631 100644 --- a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala +++ b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala @@ -4,7 +4,7 @@ import akka.actor.{Actor, ActorRef, ActorSystem, PoisonPill, Props} import akka.testkit.TestActors.ForwardActor import akka.testkit.{ImplicitSender, TestActorRef, TestFSMRef, TestKitBase, TestProbe} import beam.agentsim.agents.PersonTestUtil._ -import beam.agentsim.agents.choice.mode.ModeChoiceUniformRandom +import beam.agentsim.agents.choice.mode.{ModeChoiceUniformRandom, TourModeChoiceMultinomialLogit} import beam.agentsim.agents.household.HouseholdActor.HouseholdActor import beam.agentsim.agents.modalbehaviors.DrivesVehicle.{AlightVehicleTrigger, BoardVehicleTrigger} import beam.agentsim.agents.ridehail.{RideHailRequest, RideHailResponse} @@ -39,7 +39,7 @@ import org.matsim.households.{Household, HouseholdsFactoryImpl} import org.scalatest.BeforeAndAfter import org.scalatest.funspec.AnyFunSpecLike -import scala.collection.{mutable, JavaConverters} +import scala.collection.{JavaConverters, mutable} class PersonAgentSpec extends AnyFunSpecLike @@ -68,6 +68,8 @@ class PersonAgentSpec private lazy val modeChoiceCalculator = new ModeChoiceUniformRandom(beamConfig) + private lazy val tourModeChoiceCalculator = new TourModeChoiceMultinomialLogit(beamConfig) + // Mock a transit driver (who has to be a child of a mock router) private lazy val transitDriverProps = Props(new ForwardActor(self)) @@ -114,6 +116,7 @@ class PersonAgentSpec services, beamScenario, modeChoiceCalculator, + tourModeChoiceCalculator, beamScenario.transportNetwork, self, self, diff --git a/src/test/scala/beam/agentsim/agents/TeleportationSpec.scala b/src/test/scala/beam/agentsim/agents/TeleportationSpec.scala index 115c6366d5d..530cc2c226c 100644 --- a/src/test/scala/beam/agentsim/agents/TeleportationSpec.scala +++ b/src/test/scala/beam/agentsim/agents/TeleportationSpec.scala @@ -56,9 +56,9 @@ class TeleportationSpec extends AnyFunSpecLike with Matchers with BeamHelper wit { case _: TeleportationEvent => teleportationEvents = teleportationEvents + 1 - case e: PathTraversalEvent if e.currentTourMode.contains("car_hov3") && e.mode == BeamMode.CAR => + case e: PathTraversalEvent if e.currentTripMode.contains("car_hov3") && e.mode == BeamMode.CAR => carHov3passengers.add(e.numberOfPassengers) - case e: PathTraversalEvent if e.currentTourMode.contains("car_hov2") && e.mode == BeamMode.CAR => + case e: PathTraversalEvent if e.currentTripMode.contains("car_hov2") && e.mode == BeamMode.CAR => carHov2passengers.add(e.numberOfPassengers) case e: ActivityStartEvent if e.getPersonId.toString == "2" => activitiesOfPerson2.append((e.getLinkId.toString, e.getTime, e.getActType)) diff --git a/src/test/scala/beam/analysis/cartraveltime/StudyAreaTripFilterTest.scala b/src/test/scala/beam/analysis/cartraveltime/StudyAreaTripFilterTest.scala index 34877841bc8..e6550bd00f0 100644 --- a/src/test/scala/beam/analysis/cartraveltime/StudyAreaTripFilterTest.scala +++ b/src/test/scala/beam/analysis/cartraveltime/StudyAreaTripFilterTest.scala @@ -52,7 +52,7 @@ class StudyAreaTripFilterTest extends AnyFunSuite with Matchers { vehicleType = vehicleType, numPass = 1, beamLeg = beamLeg, - currentTourMode = None, + currentTripMode = None, primaryFuelConsumed = 1.0, secondaryFuelConsumed = 0.0, endLegPrimaryFuelLevel = 1.0, From 73476d76e1981a5dc272227ff36870c9e8cee0e8 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Thu, 25 Aug 2022 15:09:23 -0700 Subject: [PATCH 39/40] fix compilation --- src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala | 2 +- src/test/scala/beam/utils/SimRunnerForTest.scala | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala index a6546f1e631..dc8044bd44d 100644 --- a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala +++ b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala @@ -68,7 +68,7 @@ class PersonAgentSpec private lazy val modeChoiceCalculator = new ModeChoiceUniformRandom(beamConfig) - private lazy val tourModeChoiceCalculator = new TourModeChoiceMultinomialLogit(beamConfig) + private lazy val tourModeChoiceCalculator = new TourModeChoiceMultinomialLogit(attributesOfIndividual, tourModeChoiceModel) // Mock a transit driver (who has to be a child of a mock router) private lazy val transitDriverProps = Props(new ForwardActor(self)) diff --git a/src/test/scala/beam/utils/SimRunnerForTest.scala b/src/test/scala/beam/utils/SimRunnerForTest.scala index 509f4143ef0..c5e530d217f 100644 --- a/src/test/scala/beam/utils/SimRunnerForTest.scala +++ b/src/test/scala/beam/utils/SimRunnerForTest.scala @@ -1,13 +1,16 @@ package beam.utils import java.io.File - import akka.actor.ActorSystem +import beam.agentsim.agents.choice.logit.TourModeChoiceModel import beam.agentsim.agents.modalbehaviors.ModeChoiceCalculator import beam.agentsim.events.eventbuilder.{ComplexEventBuilder, EventBuilderActor} import beam.api.{BeamCustomizationAPI, DefaultAPIImplementation} +import beam.router.Modes.BeamMode import beam.sim.config.{BeamConfig, BeamConfigHolder, MatSimBeamConfigBuilder} +import beam.sim.population.{AttributesOfIndividual, HouseholdAttributes} import beam.sim.{BeamHelper, BeamScenario, BeamServices, BeamServicesImpl, RunBeam} +import com.conveyal.r5.api.util.{LegMode, TransitModes} import com.google.inject.Injector import org.matsim.core.api.experimental.events.EventsManager import org.matsim.core.config.Config @@ -27,6 +30,8 @@ trait SimRunnerForTest extends BeamHelper with BeforeAndAfterAll with BeforeAndA // Next things are pretty cheap in initialization, so let it be non-lazy val beamConfig: BeamConfig = BeamConfig(config) + val attributesOfIndividual: AttributesOfIndividual = AttributesOfIndividual(householdAttributes = HouseholdAttributes("", 0.0,0,0,0), Option(""), false, Seq(BeamMode.CAR), valueOfTime = 0.0, age = Option(0), income = Option(0)) + val tourModeChoiceModel: TourModeChoiceModel = TourModeChoiceModel(beamConfig) val matsimConfig: Config = new MatSimBeamConfigBuilder(config).buildMatSimConf() matsimConfig.controler.setOutputDirectory(outputDirPath) matsimConfig.controler.setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles) From e38c00f9725ce64faea11d6eb2f52dbabf7ec8a7 Mon Sep 17 00:00:00 2001 From: Xuan-1998 Date: Fri, 26 Aug 2022 03:43:51 -0700 Subject: [PATCH 40/40] deleting useless --- src/main/scala/beam/router/Modes.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index 40bc0d4c744..47549ec6dc5 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -262,9 +262,6 @@ object TourModes { import BeamTourMode._ - def vehicleSharedOrNot(vehicles: Vector[VehicleOrToken]): Boolean = { - vehicles.forall(_.vehicle.isSharedVehicle) - } def allowedBeamModesGivenAvailableVehicles( vehicles: Vector[VehicleOrToken],