diff --git a/src/main/java/beam/agentsim/events/ReplanningEvent.java b/src/main/java/beam/agentsim/events/ReplanningEvent.java index c04adcf69f2..339f2d0f4b3 100755 --- a/src/main/java/beam/agentsim/events/ReplanningEvent.java +++ b/src/main/java/beam/agentsim/events/ReplanningEvent.java @@ -12,14 +12,60 @@ public class ReplanningEvent extends Event implements HasPersonId { public final static String EVENT_TYPE = "Replanning"; public final static String ATTRIBUTE_PERSON = "person"; public final static String ATTRIBUTE_REPLANNING_REASON = "reason"; + public final static String ATTRIBUTE_START_COORDINATE_X = "startX"; + public final static String ATTRIBUTE_START_COORDINATE_Y = "startY"; + public final static String ATTRIBUTE_END_COORDINATE_X = "endX"; + public final static String ATTRIBUTE_END_COORDINATE_Y = "endY"; private final Id personId; private final String reason; + private final String startX; + + private final String startY; + + private final String endX; + + private final String endY; + public ReplanningEvent(final double time, final Id personId, final String reason) { super(time); this.personId = personId; this.reason = reason; + this.startX = ""; + this.startY = ""; + this.endX = ""; + this.endY = ""; + } + + public ReplanningEvent(final double time, + final Id personId, + final String reason, + final Double startX, + final Double startY, + final Double endX, + final Double endY) { + super(time); + this.personId = personId; + this.reason = reason; + this.startX = startX.toString(); + this.startY = startY.toString(); + this.endX = endX.toString(); + this.endY = endY.toString(); + } + + public ReplanningEvent(final double time, + final Id personId, + final String reason, + final Double startX, + final Double startY) { + super(time); + this.personId = personId; + this.reason = reason; + this.startX = startX.toString(); + this.startY = startY.toString(); + this.endX = ""; + this.endY = ""; } public static ReplanningEvent apply(Event event) { @@ -27,7 +73,11 @@ public static ReplanningEvent apply(Event event) { Map attr = event.getAttributes(); return new ReplanningEvent(event.getTime(), Id.createPersonId(attr.get(ATTRIBUTE_PERSON)), - attr.get(ATTRIBUTE_REPLANNING_REASON) + attr.get(ATTRIBUTE_REPLANNING_REASON), + Double.valueOf(attr.getOrDefault(ATTRIBUTE_START_COORDINATE_X, "0.0").replaceFirst("^$", "0.0")), + Double.valueOf(attr.getOrDefault(ATTRIBUTE_START_COORDINATE_Y, "0.0").replaceFirst("^$", "0.0")), + Double.valueOf(attr.getOrDefault(ATTRIBUTE_END_COORDINATE_X, "0.0").replaceFirst("^$", "0.0")), + Double.valueOf(attr.getOrDefault(ATTRIBUTE_END_COORDINATE_Y, "0.0").replaceFirst("^$", "0.0")) ); } return (ReplanningEvent) event; @@ -50,6 +100,10 @@ public Map getAttributes() { Map attr = super.getAttributes(); attr.put(ATTRIBUTE_PERSON, personId.toString()); attr.put(ATTRIBUTE_REPLANNING_REASON, reason); + attr.put(ATTRIBUTE_START_COORDINATE_X, startX); + attr.put(ATTRIBUTE_START_COORDINATE_Y, startY); + attr.put(ATTRIBUTE_END_COORDINATE_X, endX); + attr.put(ATTRIBUTE_END_COORDINATE_Y, endY); return attr; } } diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index b7d8aabcf57..5139b18b8d0 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -25,6 +25,7 @@ import beam.agentsim.events._ import beam.agentsim.events.resources.{ReservationError, ReservationErrorCode} import beam.agentsim.infrastructure.ChargingNetworkManager._ import beam.agentsim.infrastructure.parking.ParkingMNL +import beam.agentsim.infrastructure.taz.TAZ import beam.agentsim.infrastructure.{ParkingInquiryResponse, ParkingNetworkManager, ParkingStall} import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, IllegalTriggerGoToError, ScheduleTrigger} import beam.agentsim.scheduler.Trigger.TriggerWithId @@ -713,8 +714,17 @@ class PersonAgent( serviceName = response.rideHailManagerName ) ) - eventsManager.processEvent(new ReplanningEvent(tick, Id.createPersonId(id), replanningReason)) val currentCoord = beamServices.geo.wgs2Utm(data.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc + + eventsManager.processEvent( + new ReplanningEvent( + tick, + Id.createPersonId(id), + replanningReason, + currentCoord.getX, + currentCoord.getY + ) + ) val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( data.copy(currentTourMode = None, numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), @@ -740,12 +750,20 @@ class PersonAgent( ) => logDebug(s"replanning because ${firstErrorResponse.errorCode}") + val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc + val nextCoord = nextActivity(data).get.getCoord val replanningReason = getReplanningReasonFrom(data, firstErrorResponse.errorCode.entryName) eventsManager.processEvent( - new ReplanningEvent(_currentTick.get, Id.createPersonId(id), replanningReason) + new ReplanningEvent( + _currentTick.get, + Id.createPersonId(id), + replanningReason, + currentCoord.getX, + currentCoord.getY, + nextCoord.getX, + nextCoord.getY + ) ) - val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc - val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( data.copy(numberOfReplanningAttempts = data.numberOfReplanningAttempts + 1), currentLocation = SpaceTime(currentCoord, _currentTick.get), @@ -975,11 +993,18 @@ class PersonAgent( case Event(NotAvailable(_), basePersonData: BasePersonData) => log.debug("{} replanning because vehicle not available when trying to board") val replanningReason = getReplanningReasonFrom(basePersonData, ReservationErrorCode.ResourceUnavailable.entryName) - eventsManager.processEvent( - new ReplanningEvent(_currentTick.get, Id.createPersonId(id), replanningReason) - ) val currentCoord = beamServices.geo.wgs2Utm(basePersonData.restOfCurrentTrip.head.beamLeg.travelPath.startPoint).loc + eventsManager.processEvent( + new ReplanningEvent( + _currentTick.get, + Id.createPersonId(id), + replanningReason, + currentCoord.getX, + currentCoord.getY + ) + ) + val nextCoord = nextActivity(basePersonData).get.getCoord goto(ChoosingMode) using ChoosesModeData( basePersonData.copy( @@ -1168,10 +1193,17 @@ class PersonAgent( log.debug("Missed transit pickup, late by {} sec", _currentTick.get - nextLeg.beamLeg.startTime) val replanningReason = getReplanningReasonFrom(data, ReservationErrorCode.MissedTransitPickup.entryName) + val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc eventsManager.processEvent( - new ReplanningEvent(_currentTick.get, Id.createPersonId(id), replanningReason) + new ReplanningEvent( + _currentTick.get, + Id.createPersonId(id), + replanningReason, + currentCoord.getX, + currentCoord.getY + ) ) - val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc + val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data @@ -1234,10 +1266,17 @@ class PersonAgent( log.warning("Missed CAV pickup, late by {} sec", _currentTick.get - nextLeg.beamLeg.startTime) val replanningReason = getReplanningReasonFrom(data, ReservationErrorCode.MissedTransitPickup.entryName) + val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc eventsManager.processEvent( - new ReplanningEvent(_currentTick.get, Id.createPersonId(id), replanningReason) + new ReplanningEvent( + _currentTick.get, + Id.createPersonId(id), + replanningReason, + currentCoord.getX, + currentCoord.getY + ) ) - val currentCoord = beamServices.geo.wgs2Utm(nextLeg.beamLeg.travelPath.startPoint).loc + val nextCoord = nextActivity(data).get.getCoord goto(ChoosingMode) using ChoosesModeData( personData = data @@ -1460,6 +1499,24 @@ class PersonAgent( } } + def getTazFromActivity(activity: Activity): Id[TAZ] = { + val linkId = Option(activity.getLinkId).getOrElse( + Id.createLinkId( + beamServices.geo + .getNearestR5EdgeToUTMCoord( + beamServices.beamScenario.transportNetwork.streetLayer, + activity.getCoord, + beamScenario.beamConfig.beam.routing.r5.linkRadiusMeters + ) + .toString + ) + ) + beamScenario.tazTreeMap + .getTAZfromLink(linkId) + .map(_.tazId) + .getOrElse(beamScenario.tazTreeMap.getTAZ(activity.getCoord).tazId) + } + def generateSkimData( tick: Int, trip: EmbodiedBeamTrip, @@ -1489,18 +1546,10 @@ class PersonAgent( ) eventsManager.processEvent(odSkimmerEvent) if (beamServices.beamConfig.beam.exchange.output.activitySimSkimsEnabled) { - val startLink = currentActivity.getLinkId - val endLinkOption = nextActivity.map(_.getLinkId) val (origin, destination) = - if (beamScenario.tazTreeMap.tazListContainsGeoms && endLinkOption.isDefined) { - val origGeo = beamScenario.tazTreeMap - .getTAZfromLink(startLink) - .map(_.tazId.toString) - .getOrElse("NA") - val destGeo = beamScenario.tazTreeMap - .getTAZfromLink(endLinkOption.get) - .map(_.tazId.toString) - .getOrElse("NA") + if (beamScenario.tazTreeMap.tazListContainsGeoms) { + val origGeo = getTazFromActivity(currentActivity).toString + val destGeo = nextActivity.map(getTazFromActivity(_).toString).getOrElse("NA") (origGeo, destGeo) } else { beamScenario.exchangeGeoMap match { diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 4e9d03959c5..58ac9a7a312 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -16,14 +16,17 @@ import beam.agentsim.agents.vehicles.VehicleProtocol.StreetVehicle import beam.agentsim.agents.vehicles._ import beam.agentsim.events.resources.ReservationErrorCode import beam.agentsim.events.{ModeChoiceEvent, ReplanningEvent, SpaceTime} +import beam.agentsim.infrastructure.taz.TAZ import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse, ZonalParkingManager} 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.model.{BeamLeg, EmbodiedBeamLeg, EmbodiedBeamTrip} +import beam.router.skim.ActivitySimPathType.determineActivitySimPathTypesFromBeamMode +import beam.router.skim.{ActivitySimPathType, ActivitySimSkimmerFailedTripEvent} import beam.router.skim.core.ODSkimmer -import beam.router.skim.event.ODSkimmerFailedTripEvent +import beam.router.skim.event.{ODSkimmerEvent, ODSkimmerFailedTripEvent} import beam.router.skim.readonly.ODSkims import beam.router.{Modes, RoutingWorker} import beam.sim.population.AttributesOfIndividual @@ -505,7 +508,9 @@ trait ChoosesMode { getReplanningReasonFrom( choosesModeData.personData, ReservationErrorCode.HouseholdVehicleNotAvailable.entryName - ) + ), + currentPersonLocation.loc.getX, + currentPersonLocation.loc.getY ) ) householdVehiclesWereNotAvailable = true @@ -1256,16 +1261,6 @@ trait ChoosesMode { parkingResponses ) ++ rideHail2TransitIinerary.toVector - def isAvailable(mode: BeamMode): Boolean = combinedItinerariesForChoice.exists(_.tripClassifier == mode) - - choosesModeData.personData.currentTourMode match { - case Some(expectedMode) if expectedMode.isTransit && !isAvailable(expectedMode) => - eventsManager.processEvent( - createFailedTransitODSkimmerEvent(currentPersonLocation.loc, nextAct.getCoord, expectedMode) - ) - case _ => - } - val availableModesForTrips: Seq[BeamMode] = availableModesForPerson(matsimPlan.getPerson) .filterNot(mode => choosesModeData.excludeModes.contains(mode)) @@ -1377,6 +1372,32 @@ trait ChoosesMode { ) } case Some(mode) => + val currentAct = currentActivity(personData) + val odFailedSkimmerEvent = createFailedODSkimmerEvent(currentAct, nextAct, mode) + val possibleActivitySimModes = + determineActivitySimPathTypesFromBeamMode(choosesModeData.personData.currentTourMode, currentAct) + eventsManager.processEvent( + odFailedSkimmerEvent + ) + if (beamServices.beamConfig.beam.exchange.output.activitySimSkimsEnabled) { + createFailedActivitySimSkimmerEvent(odFailedSkimmerEvent, possibleActivitySimModes).foreach(ev => + eventsManager.processEvent(ev) + ) + } + eventsManager.processEvent( + new ReplanningEvent( + _currentTick.get, + Id.createPersonId(id), + getReplanningReasonFrom( + choosesModeData.personData, + ReservationErrorCode.RouteNotAvailableForChosenMode.entryName + ), + choosesModeData.currentLocation.loc.getX, + choosesModeData.currentLocation.loc.getY, + nextAct.getCoord.getX, + nextAct.getCoord.getY + ) + ) //give another chance to make a choice without predefined mode val availableVehicles = if (mode.isTeleportation) @@ -1421,38 +1442,56 @@ trait ChoosesMode { } } - private def createFailedTransitODSkimmerEvent( - originLocation: Location, - destinationLocation: Location, + private def createFailedODSkimmerEvent( + originActivity: Activity, + destinationActivity: Activity, mode: BeamMode ): ODSkimmerFailedTripEvent = { - val skim: ODSkimmer.Skim = ODSkims.getSkimDefaultValue( - beamServices.beamConfig, - mode, - "", - originLocation, - destinationLocation, - beamScenario.vehicleTypes(dummyRHVehicle.vehicleTypeId), - 0 - ) + val (origCoord, destCoord) = (originActivity.getCoord, destinationActivity.getCoord) + val (origin, destination) = + if (beamScenario.tazTreeMap.tazListContainsGeoms) { + val startTaz = getTazFromActivity(originActivity) + val endTaz = getTazFromActivity(destinationActivity) + (startTaz.toString, endTaz.toString) + } else { + beamScenario.exchangeGeoMap match { + case Some(geoMap) => + val origGeo = geoMap.getTAZ(origCoord) + val destGeo = geoMap.getTAZ(destCoord) + (origGeo.tazId.toString, destGeo.tazId.toString) + case None => + val origGeo = beamScenario.tazTreeMap.getTAZ(origCoord) + val destGeo = beamScenario.tazTreeMap.getTAZ(destCoord) + (origGeo.tazId.toString, destGeo.tazId.toString) + } + } - val origTazId = beamScenario.tazTreeMap - .getTAZ(originLocation.getX, originLocation.getY) - .tazId - val destTazId = beamScenario.tazTreeMap - .getTAZ(destinationLocation.getX, destinationLocation.getY) - .tazId ODSkimmerFailedTripEvent( - origin = origTazId.toString, - destination = destTazId.toString, + origin = origin, + destination = destination, eventTime = _currentTick.get, mode = mode, - skim, beamServices.matsimServices.getIterationNumber, skimName = beamServices.beamConfig.beam.router.skim.origin_destination_skimmer.name ) } + private def createFailedActivitySimSkimmerEvent( + failedODSkimmerEvent: ODSkimmerFailedTripEvent, + modes: Seq[ActivitySimPathType] + ): Seq[ActivitySimSkimmerFailedTripEvent] = { + modes.map { pathType => + ActivitySimSkimmerFailedTripEvent( + origin = failedODSkimmerEvent.origin, + destination = failedODSkimmerEvent.destination, + eventTime = _currentTick.get, + activitySimPathType = pathType, + iterationNumber = beamServices.matsimServices.getIterationNumber, + skimName = beamServices.beamConfig.beam.router.skim.activity_sim_skimmer.name + ) + } + } + private def allRequiredParkingResponsesReceived( routingResponse: RoutingResponse, parkingResponses: Map[VehicleOnTrip, ParkingInquiryResponse] diff --git a/src/main/scala/beam/agentsim/events/resources/AccessRequest.scala b/src/main/scala/beam/agentsim/events/resources/AccessRequest.scala index 119d373f8de..ee7ed149c18 100755 --- a/src/main/scala/beam/agentsim/events/resources/AccessRequest.scala +++ b/src/main/scala/beam/agentsim/events/resources/AccessRequest.scala @@ -76,4 +76,6 @@ case object ReservationErrorCode extends Enum[ReservationErrorCode] { case object HouseholdVehicleNotAvailable extends ReservationErrorCode + case object RouteNotAvailableForChosenMode extends ReservationErrorCode + } diff --git a/src/main/scala/beam/router/skim/ActivitySimPathType.scala b/src/main/scala/beam/router/skim/ActivitySimPathType.scala index 50db4a9bfe5..70ec0b72db4 100644 --- a/src/main/scala/beam/router/skim/ActivitySimPathType.scala +++ b/src/main/scala/beam/router/skim/ActivitySimPathType.scala @@ -2,6 +2,7 @@ package beam.router.skim import beam.router.Modes.BeamMode import beam.router.model.{EmbodiedBeamLeg, EmbodiedBeamTrip} +import org.matsim.api.core.v01.population.Activity sealed trait ActivitySimPathType @@ -150,6 +151,69 @@ object ActivitySimPathType { } } + def determineActivitySimPathTypesFromBeamMode( + currentMode: Option[BeamMode], + currentActivity: Activity + ): Seq[ActivitySimPathType] = { + val currentActivityType = currentActivity.getType.toLowerCase() + currentMode match { + case Some(BeamMode.WALK) => Seq(ActivitySimPathType.WALK) + case Some(BeamMode.CAR) => + // Note: Attempt to future-proof this in case there are some routes that can only be accomplished by HOVs. + // The reverse shouldn't ever be the case, where a route cant be accomplished by HOVs + Seq( + ActivitySimPathType.SOV, + ActivitySimPathType.SOVTOLL + ) + case Some(BeamMode.CAR_HOV2) => + Seq( + ActivitySimPathType.HOV2, + ActivitySimPathType.HOV2TOLL, + ActivitySimPathType.SOV, + ActivitySimPathType.SOVTOLL + ) + case Some(BeamMode.CAR_HOV3) => + Seq( + ActivitySimPathType.HOV3, + ActivitySimPathType.HOV3TOLL, + ActivitySimPathType.HOV2, + ActivitySimPathType.HOV2TOLL, + ActivitySimPathType.SOV, + ActivitySimPathType.SOVTOLL + ) + case Some(BeamMode.WALK_TRANSIT) => + Seq( + ActivitySimPathType.WLK_LOC_WLK, + ActivitySimPathType.WLK_HVY_WLK, + ActivitySimPathType.WLK_COM_WLK, + ActivitySimPathType.WLK_LRF_WLK, + ActivitySimPathType.WLK_EXP_WLK, + ActivitySimPathType.WLK_TRN_WLK + ) + case Some(BeamMode.DRIVE_TRANSIT) => + currentActivityType match { + case "home" => + Seq( + ActivitySimPathType.DRV_LOC_WLK, + ActivitySimPathType.DRV_HVY_WLK, + ActivitySimPathType.DRV_COM_WLK, + ActivitySimPathType.DRV_LRF_WLK, + ActivitySimPathType.DRV_EXP_WLK + ) + case _ => + Seq( + ActivitySimPathType.WLK_LOC_DRV, + ActivitySimPathType.WLK_HVY_DRV, + ActivitySimPathType.WLK_COM_DRV, + ActivitySimPathType.WLK_LRF_DRV, + ActivitySimPathType.WLK_EXP_DRV + ) + } + case _ => + Seq.empty[ActivitySimPathType] + } + } + val allPathTypes: Seq[ActivitySimPathType] = Seq( DRV_COM_WLK, DRV_HVY_WLK, diff --git a/src/main/scala/beam/router/skim/ActivitySimSkimmer.scala b/src/main/scala/beam/router/skim/ActivitySimSkimmer.scala index 7b4568293af..238982a3d30 100644 --- a/src/main/scala/beam/router/skim/ActivitySimSkimmer.scala +++ b/src/main/scala/beam/router/skim/ActivitySimSkimmer.scala @@ -46,7 +46,9 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario val prevSkim = prevIteration .map(_.asInstanceOf[ActivitySimSkimmerInternal]) - .getOrElse(ActivitySimSkimmerInternal(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, observations = 0)) + .getOrElse( + ActivitySimSkimmerInternal(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, observations = 0) + ) val currSkim = currIteration .map(_.asInstanceOf[ActivitySimSkimmerInternal]) @@ -98,6 +100,8 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario ferryInVehicleTimeInMinutes = aggregate(_.ferryInVehicleTimeInMinutes), keyInVehicleTimeInMinutes = aggregate(_.keyInVehicleTimeInMinutes), transitBoardingsCount = aggregate(_.transitBoardingsCount), + failedTrips = + (prevSkim.failedTrips * prevSkim.iterations + currSkim.failedTrips * currSkim.iterations) / (prevSkim.iterations + currSkim.iterations), observations = (prevSkim.observations * prevSkim.iterations + currSkim.observations * currSkim.iterations) / (prevSkim.iterations + currSkim.iterations), iterations = prevSkim.iterations + currSkim.iterations @@ -137,6 +141,7 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario ferryInVehicleTimeInMinutes = aggregatedDoubleSkimValue(_.ferryInVehicleTimeInMinutes), keyInVehicleTimeInMinutes = aggregatedDoubleSkimValue(_.keyInVehicleTimeInMinutes), transitBoardingsCount = aggregatedDoubleSkimValue(_.transitBoardingsCount), + failedTrips = prevSkim.failedTrips + currSkim.failedTrips, observations = prevSkim.observations + currSkim.observations, iterations = matsimServices.getIterationNumber + 1, debugText = Seq(prevSkim.debugText, currSkim.debugText).mkString("|") @@ -266,6 +271,8 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario val weightedKeyInVehicleTime = getWeightedSkimsValue(_.keyInVehicleTimeInMinutes) val weightedFerryTime = getWeightedSkimsValue(_.ferryInVehicleTimeInMinutes) val weightedTransitBoardingsCount = getWeightedSkimsValue(_.transitBoardingsCount) + val failedTrips = individualSkims.map(_.failedTrips).sum + val completedTrips = individualSkims.map(_.observations).sum val debugText = individualSkims.map(_.debugText).filter(t => t != "").mkString("|") ExcerptData( @@ -288,6 +295,8 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario weightedFerryInVehicleTimeInMinutes = weightedFerryTime, weightedTransitBoardingsCount = weightedTransitBoardingsCount, weightedCost = weightedCost, + failedTrips = failedTrips, + completedTrips = completedTrips, debugText = debugText ) } @@ -319,6 +328,8 @@ class ActivitySimSkimmer @Inject() (matsimServices: MatsimServices, beamScenario 0, 0, 0, + 0, + 0, "" ) ) @@ -352,6 +363,7 @@ object ActivitySimSkimmer extends LazyLogging { ferryInVehicleTimeInMinutes: Double, keyInVehicleTimeInMinutes: Double, transitBoardingsCount: Double, + failedTrips: Int = 0, observations: Int = 1, iterations: Int = 0, debugText: String = "" @@ -359,13 +371,13 @@ object ActivitySimSkimmer extends LazyLogging { override def toCsv: String = travelTimeInMinutes + "," + generalizedTimeInMinutes + "," + cost + "," + generalizedCost + "," + - distanceInMeters + "," + energy + "," + observations + "," + iterations + distanceInMeters + "," + energy + "," + failedTrips + "," + observations + "," + iterations } object ActivitySimSkimmerInternal { def empty: ActivitySimSkimmerInternal = - ActivitySimSkimmerInternal(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + ActivitySimSkimmerInternal(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) } case class ExcerptData( @@ -388,6 +400,8 @@ object ActivitySimSkimmer extends LazyLogging { weightedFerryInVehicleTimeInMinutes: Double, weightedTransitBoardingsCount: Double, weightedCost: Double, + failedTrips: Int, + completedTrips: Int, debugText: String = "" ) { @@ -418,6 +432,8 @@ object ActivitySimSkimmer extends LazyLogging { "FERRYIVT_minutes", "BOARDS", "WeightedCost", + "failedTrips", + "completedTrips", "DEBUG_TEXT" ) diff --git a/src/main/scala/beam/router/skim/ActivitySimSkimmerEvent.scala b/src/main/scala/beam/router/skim/ActivitySimSkimmerEvent.scala index b853d507880..a5f07ef1697 100644 --- a/src/main/scala/beam/router/skim/ActivitySimSkimmerEvent.scala +++ b/src/main/scala/beam/router/skim/ActivitySimSkimmerEvent.scala @@ -125,8 +125,11 @@ case class ActivitySimSkimmerEvent( travelTimeInMinutes = trip.totalTravelTimeInSecs.toDouble / 60.0, generalizedTimeInMinutes = generalizedTimeInHours * 60, generalizedCost = generalizedCost, - distanceInMeters = if (distInMeters > 0.0) { distInMeters } - else { 1.0 }, + distanceInMeters = if (distInMeters > 0.0) { + distInMeters + } else { + 1.0 + }, cost = trip.costEstimate, energy = energyConsumption, walkAccessInMinutes = walkAccess / 60.0, @@ -139,12 +142,51 @@ case class ActivitySimSkimmerEvent( driveDistanceInMeters = driveDistanceInMeters, ferryInVehicleTimeInMinutes = ferryTimeInSeconds / 60.0, keyInVehicleTimeInMinutes = keyInVehicleTimeInSeconds / 60.0, - transitBoardingsCount = numberOfTransitTrips + transitBoardingsCount = numberOfTransitTrips, + failedTrips = 0, + observations = 1 ) (key, payload) } } +case class ActivitySimSkimmerFailedTripEvent( + origin: String, + destination: String, + eventTime: Double, + activitySimPathType: ActivitySimPathType, + iterationNumber: Int, + override val skimName: String +) extends AbstractSkimmerEvent(eventTime) { + + override def getKey: ActivitySimSkimmerKey = + ActivitySimSkimmerKey(SkimsUtils.timeToBin(Math.round(eventTime).toInt), activitySimPathType, origin, destination) + + override def getSkimmerInternal: ActivitySimSkimmerInternal = { + ActivitySimSkimmerInternal( + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + failedTrips = 1, + observations = 0 + ) + } +} + object ActivitySimSkimmerEvent { val carModes: Set[BeamMode] = Set(BeamMode.CAV, BeamMode.CAR, BeamMode.RIDE_HAIL, BeamMode.RIDE_HAIL_POOLED) diff --git a/src/main/scala/beam/router/skim/core/ODSkimmer.scala b/src/main/scala/beam/router/skim/core/ODSkimmer.scala index 7251d487d20..1d6dfb025a5 100644 --- a/src/main/scala/beam/router/skim/core/ODSkimmer.scala +++ b/src/main/scala/beam/router/skim/core/ODSkimmer.scala @@ -420,7 +420,7 @@ object ODSkimmer extends LazyLogging { energy = Option(row("energy")).map(_.toDouble).getOrElse(0.0), payloadWeightInKg = row.get("payloadWeightInKg").map(_.toDouble).getOrElse(0.0), level4CavTravelTimeScalingFactor = row.get("level4CavTravelTimeScalingFactor").map(_.toDouble).getOrElse(1.0), - failedTrips = row.get("failedTrips").map(_.toDouble).getOrElse(0.0), + failedTrips = NumberUtils.toInt(row("failedTrips"), 0), observations = NumberUtils.toInt(row("observations"), 0), iterations = NumberUtils.toInt(row("iterations"), 1) ) @@ -436,7 +436,7 @@ object ODSkimmer extends LazyLogging { payloadWeightInKg: Double, energy: Double, level4CavTravelTimeScalingFactor: Double, - failedTrips: Double, + failedTrips: Int = 0, observations: Int = 1, iterations: Int = 0 ) extends AbstractSkimmerInternal { diff --git a/src/main/scala/beam/router/skim/event/ODSkimmerEvent.scala b/src/main/scala/beam/router/skim/event/ODSkimmerEvent.scala index b40096ea1ba..42376283324 100644 --- a/src/main/scala/beam/router/skim/event/ODSkimmerEvent.scala +++ b/src/main/scala/beam/router/skim/event/ODSkimmerEvent.scala @@ -140,7 +140,6 @@ case class ODSkimmerFailedTripEvent( destination: String, eventTime: Double, mode: BeamMode, - skim: ODSkimmer.Skim, iterationNumber: Int, override val skimName: String ) extends AbstractSkimmerEvent(eventTime) { @@ -150,15 +149,16 @@ case class ODSkimmerFailedTripEvent( override def getSkimmerInternal: AbstractSkimmerInternal = { ODSkimmerInternal( - skim.time, - skim.generalizedTime, - skim.generalizedCost, - skim.distance, - skim.cost, - skim.payloadWeight, - skim.energy, - skim.level4CavTravelTimeScalingFactor, - failedTrips = 1 + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + failedTrips = 1, + observations = 0 ) } } diff --git a/src/main/scala/beam/scoring/BeamScoringFunctionFactory.scala b/src/main/scala/beam/scoring/BeamScoringFunctionFactory.scala index 31feac526c4..e99d7d8b184 100755 --- a/src/main/scala/beam/scoring/BeamScoringFunctionFactory.scala +++ b/src/main/scala/beam/scoring/BeamScoringFunctionFactory.scala @@ -91,7 +91,8 @@ class BeamScoringFunctionFactory @Inject() ( s"a replanning. Value if replanOnTheFlyWhenHouseholdVehiclesAreNotAvailable is " + s"${beamConfig.beam.agentsim.agents.vehicles.replanOnTheFlyWhenHouseholdVehiclesAreNotAvailable}" ) - } else { + } else if (!e.getReason.startsWith("RouteNotAvailableForChosenMode")) { + // Don't need to remove a trip if a route wasn't found because no initial trip was taken logger.error( f"We need to remove a trip, but the trips collection is empty, this might be a bug. " + f"The event: ${e.toString}, the person: ${person.getId.toString}" diff --git a/src/main/scala/beam/sim/common/GeoUtils.scala b/src/main/scala/beam/sim/common/GeoUtils.scala index 371c8c68988..a52a3b3b31b 100755 --- a/src/main/scala/beam/sim/common/GeoUtils.scala +++ b/src/main/scala/beam/sim/common/GeoUtils.scala @@ -55,7 +55,15 @@ trait GeoUtils extends ExponentialLazyLogging { def utm2Wgs(spacetime: SpaceTime): SpaceTime = SpaceTime(utm2Wgs(spacetime.loc), spacetime.time) def utm2Wgs(coord: Coord): Coord = { - utm2Wgs.transform(coord) + try { + utm2Wgs.transform(coord) + } catch { + case e: Throwable => + logger.warn(e.getMessage) + logger.warn(s"Coordinate cannot be converged from UTM -> WGS. No conversion will happen: $coord") + coord + } + } def distUTMInMeters(coord1: Coord, coord2: Coord): Double = GeoUtils.distUTMInMeters(coord1, coord2) diff --git a/src/main/scala/beam/sim/vehiclesharing/AvailabilityBasedRepositioning.scala b/src/main/scala/beam/sim/vehiclesharing/AvailabilityBasedRepositioning.scala index 7ded32bb865..4f18b3e5194 100644 --- a/src/main/scala/beam/sim/vehiclesharing/AvailabilityBasedRepositioning.scala +++ b/src/main/scala/beam/sim/vehiclesharing/AvailabilityBasedRepositioning.scala @@ -29,9 +29,10 @@ case class AvailabilityBasedRepositioning( (0 to 108000 / repositionTimeBin).foreach { i => val time = i * repositionTimeBin val availVal = getCollectedDataFromPreviousSimulation(time, taz.tazId, RepositionManager.availability) - val availValMin = availVal.drop(1).foldLeft(availVal.headOption.map(_.observations).getOrElse(0)) { (minV, cur) => - Math.min(minV, cur.observations) - } + val availValMin = + availVal.drop(1).foldLeft(availVal.headOption.map(_.observations).getOrElse(0)) { (minV, cur) => + Math.min(minV, cur.observations) + } minAvailabilityMap.put((i, taz.tazId), availValMin) val inquiryVal = getCollectedDataFromPreviousSimulation(time, taz.tazId, RepositionManager.inquiry).map(_.observations).sum diff --git a/src/main/scala/beam/sim/vehiclesharing/AvailabilityBehaviorBasedRepositioning.scala b/src/main/scala/beam/sim/vehiclesharing/AvailabilityBehaviorBasedRepositioning.scala index 0eb98af9cab..e23fe5d61c9 100644 --- a/src/main/scala/beam/sim/vehiclesharing/AvailabilityBehaviorBasedRepositioning.scala +++ b/src/main/scala/beam/sim/vehiclesharing/AvailabilityBehaviorBasedRepositioning.scala @@ -29,9 +29,10 @@ case class AvailabilityBehaviorBasedRepositioning( (0 to 108000 / repositionTimeBin).foreach { i => val time = i * repositionTimeBin val availVal = getCollectedDataFromPreviousSimulation(time, taz.tazId, RepositionManager.availability) - val availValMin = availVal.drop(1).foldLeft(availVal.headOption.map(_.observations).getOrElse(0)) { (minV, cur) => - Math.min(minV, cur.observations) - } + val availValMin = + availVal.drop(1).foldLeft(availVal.headOption.map(_.observations).getOrElse(0)) { (minV, cur) => + Math.min(minV, cur.observations) + } minAvailabilityMap.put((i, taz.tazId), availValMin) val inquiryVal = getCollectedDataFromPreviousSimulation(time, taz.tazId, RepositionManager.inquiry).map(_.observations).sum diff --git a/src/main/scala/scripts/BackgroundSkimsCreatorApp.scala b/src/main/scala/scripts/BackgroundSkimsCreatorApp.scala index 6f20b4d3a10..e5c66bb2597 100644 --- a/src/main/scala/scripts/BackgroundSkimsCreatorApp.scala +++ b/src/main/scala/scripts/BackgroundSkimsCreatorApp.scala @@ -99,20 +99,22 @@ object BackgroundSkimsCreatorApp extends App with BeamHelper { originId = rec.get("origin"), destinationId = rec.get("destination"), weightedTotalTime = rec.get("TIME_minutes").toDouble, + weightedTotalInVehicleTime = rec.get("TOTIVT_IVT_minutes").toDouble, weightedTotalCost = rec.get("VTOLL_FAR").toDouble, weightedDistance = rec.get("DIST_meters").toDouble, weightedWalkAccess = rec.get("WACC_minutes").toDouble, - weightedWalkEgress = rec.get("WEGR_minutes").toDouble, weightedWalkAuxiliary = rec.get("WAUX_minutes").toDouble, + weightedWalkEgress = rec.get("WEGR_minutes").toDouble, weightedWaitInitial = rec.getOrDefault("IWAIT_minutes", "0").toDouble, weightedWaitTransfer = rec.getOrDefault("XWAIT_minutes", "0").toDouble, - weightedTotalInVehicleTime = rec.get("TOTIVT_IVT_minutes").toDouble, weightedDriveTimeInMinutes = rec.get("DTIM_minutes").toDouble, weightedDriveDistanceInMeters = rec.get("DDIST_meters").toDouble, - weightedFerryInVehicleTimeInMinutes = rec.get("FERRYIVT_minutes").toDouble, weightedKeyInVehicleTimeInMinutes = rec.get("KEYIVT_minutes").toDouble, + weightedFerryInVehicleTimeInMinutes = rec.get("FERRYIVT_minutes").toDouble, weightedTransitBoardingsCount = rec.get("BOARDS").toDouble, weightedCost = Option(rec.get("WeightedCost")).map(_.toDouble).getOrElse(0.0d), + failedTrips = Option(rec.get("FailedTrips")).map(_.toInt).getOrElse(0), + completedTrips = Option(rec.get("CompletedTrips")).map(_.toInt).getOrElse(0), debugText = rec.get("DEBUG_TEXT") ) diff --git a/src/test/scala/beam/router/skim/ActivitySimSkimmerEventTest.scala b/src/test/scala/beam/router/skim/ActivitySimSkimmerEventTest.scala index 08be1b67467..f26c7cbe056 100644 --- a/src/test/scala/beam/router/skim/ActivitySimSkimmerEventTest.scala +++ b/src/test/scala/beam/router/skim/ActivitySimSkimmerEventTest.scala @@ -2,7 +2,9 @@ package beam.router.skim import beam.router.Modes.BeamMode import beam.router.model.{BeamLeg, BeamPath, EmbodiedBeamLeg, EmbodiedBeamTrip} -import org.matsim.api.core.v01.Id +import org.matsim.api.core.v01.{Coord, Id} +import org.matsim.api.core.v01.population.Activity +import org.matsim.core.population.PopulationUtils import org.mockito.Mockito.when import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -22,6 +24,10 @@ class ActivitySimSkimmerEventTest extends AnyFlatSpec with Matchers { leg } + def mockActivity(activityType: String): Activity = { + PopulationUtils.createActivityFromCoord(activityType, new Coord(0, 0)) + } + def walkLeg10: EmbodiedBeamLeg = mockLeg(10, BeamMode.WALK) def walkLeg15: EmbodiedBeamLeg = mockLeg(15, BeamMode.WALK) def carLeg10: EmbodiedBeamLeg = mockLeg(10, BeamMode.CAR) @@ -30,6 +36,9 @@ class ActivitySimSkimmerEventTest extends AnyFlatSpec with Matchers { def busLeg15: EmbodiedBeamLeg = mockLeg(15, BeamMode.BUS) def railLeg15: EmbodiedBeamLeg = mockLeg(15, BeamMode.SUBWAY) + def homeActivity: Activity = mockActivity("Home") + def workActivity: Activity = mockActivity("Work") + "skimmer event" should "parse trip 1" in { val trip = new EmbodiedBeamTrip( IndexedSeq(walkLeg10, walkLeg15, busLeg10, walkLeg15, busLeg10, walkLeg15, carLeg10, walkLeg10) @@ -98,4 +107,95 @@ class ActivitySimSkimmerEventTest extends AnyFlatSpec with Matchers { event.skimInternal.keyInVehicleTimeInMinutes shouldBe 15 / 60.0 event.key.pathType shouldBe ActivitySimPathType.WLK_HVY_WLK } + + "skimmer event" should "parse failed trip 1" in { + val pathType = ActivitySimPathType.determineActivitySimPathTypesFromBeamMode(Some(BeamMode.CAR), homeActivity) + pathType.length should be > 1 + val event = ActivitySimSkimmerFailedTripEvent( + "o1", + "d1", + 10 * 60 * 60, + pathType.filter { path => path == ActivitySimPathType.SOV }.head, + 0, + "skimname" + ) + event.getSkimmerInternal.walkAccessInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkEgressInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkAuxiliaryInMinutes shouldBe 0.0 + event.getSkimmerInternal.totalInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.keyInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.failedTrips shouldBe 1 + event.getSkimmerInternal.observations shouldBe 0 + event.getKey.pathType shouldBe ActivitySimPathType.SOV + } + + "skimmer event" should "parse failed trip 2" in { + val pathType = + ActivitySimPathType.determineActivitySimPathTypesFromBeamMode(Some(BeamMode.WALK_TRANSIT), homeActivity) + pathType.length should be > 1 + val event = ActivitySimSkimmerFailedTripEvent( + "o1", + "d1", + 10 * 60 * 60, + pathType.filter { path => path == ActivitySimPathType.WLK_LOC_WLK }.head, + 0, + "skimname" + ) + event.getSkimmerInternal.walkAccessInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkEgressInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkAuxiliaryInMinutes shouldBe 0.0 + event.getSkimmerInternal.totalInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.keyInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.failedTrips shouldBe 1 + event.getSkimmerInternal.observations shouldBe 0 + event.getKey.pathType shouldBe ActivitySimPathType.WLK_LOC_WLK + } + + "skimmer event" should "order access legs correctly in trip 3" in { + val pathType = + ActivitySimPathType.determineActivitySimPathTypesFromBeamMode(Some(BeamMode.DRIVE_TRANSIT), homeActivity) + pathType.length should be > 1 + // If we're starting a DRIVE_TRANSIT trip at home, we assume the car is used for access but not egress + pathType.count(_ == ActivitySimPathType.WLK_LOC_DRV) shouldBe 0 + val event = ActivitySimSkimmerFailedTripEvent( + "o1", + "d1", + 10 * 60 * 60, + pathType.filter { path => path == ActivitySimPathType.DRV_LOC_WLK }.head, + 0, + "skimname" + ) + event.getSkimmerInternal.walkAccessInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkEgressInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkAuxiliaryInMinutes shouldBe 0.0 + event.getSkimmerInternal.totalInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.keyInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.failedTrips shouldBe 1 + event.getSkimmerInternal.observations shouldBe 0 + event.getKey.pathType shouldBe ActivitySimPathType.DRV_LOC_WLK + } + + "skimmer event" should "order access legs correctly in trip 4" in { + val pathType = + ActivitySimPathType.determineActivitySimPathTypesFromBeamMode(Some(BeamMode.DRIVE_TRANSIT), workActivity) + pathType.length should be > 1 + // If we're starting a DRIVE_TRANSIT trip at work, we assume the car is used for egress but not access + pathType.count(_ == ActivitySimPathType.DRV_LOC_WLK) shouldBe 0 + val event = ActivitySimSkimmerFailedTripEvent( + "o1", + "d1", + 10 * 60 * 60, + pathType.filter { path => path == ActivitySimPathType.WLK_LOC_DRV }.head, + 0, + "skimname" + ) + event.getSkimmerInternal.walkAccessInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkEgressInMinutes shouldBe 0.0 + event.getSkimmerInternal.walkAuxiliaryInMinutes shouldBe 0.0 + event.getSkimmerInternal.totalInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.keyInVehicleTimeInMinutes shouldBe 0.0 + event.getSkimmerInternal.failedTrips shouldBe 1 + event.getSkimmerInternal.observations shouldBe 0 + event.getKey.pathType shouldBe ActivitySimPathType.WLK_LOC_DRV + } } diff --git a/src/test/scala/beam/router/skim/ActivitySimSkimmerTest.scala b/src/test/scala/beam/router/skim/ActivitySimSkimmerTest.scala index b3f644c7aef..f5444c70ca0 100644 --- a/src/test/scala/beam/router/skim/ActivitySimSkimmerTest.scala +++ b/src/test/scala/beam/router/skim/ActivitySimSkimmerTest.scala @@ -29,9 +29,11 @@ class ActivitySimSkimmerTest extends AnyFlatSpec with Matchers { weightedFerryInVehicleTimeInMinutes = 11.0, weightedTransitBoardingsCount = 12.0, weightedCost = 13.0, + failedTrips = 0, + completedTrips = 1, debugText = "debug-text" ).toCsvString should be( - "MD,WALK,origin-1,destination-1,1.0,2.0,3.0,4.0,5.0,6.0,7.0,1.0,2.0,8.0,9.0,10.0,11.0,12.0,13.0,debug-text\n" + "MD,WALK,origin-1,destination-1,1.0,2.0,3.0,4.0,5.0,6.0,7.0,1.0,2.0,8.0,9.0,10.0,11.0,12.0,13.0,0,1,debug-text\n" ) } } diff --git a/src/test/scala/beam/router/skim/CsvSkimReaderSpec.scala b/src/test/scala/beam/router/skim/CsvSkimReaderSpec.scala index 479cb1be2c6..4a871cd1c8d 100644 --- a/src/test/scala/beam/router/skim/CsvSkimReaderSpec.scala +++ b/src/test/scala/beam/router/skim/CsvSkimReaderSpec.scala @@ -35,6 +35,7 @@ class CsvSkimReaderSpec extends AnyFlatSpec with Matchers with BeamHelper { checkSkimmerField[Double, ODSkimmerInternal]("generalizedCost", 2, 10, skims.values, x => x.generalizedCost) checkSkimmerField[Double, ODSkimmerInternal]("distanceInM", 1175.0, 2500, skims.values, x => x.distanceInM) checkSkimmerField[Double, ODSkimmerInternal]("energy", 10, 2000, skims.values, x => x.energy) + checkSkimmerField[Int, ODSkimmerInternal]("failedTrips", 0, 1, skims.values, x => x.failedTrips) checkSkimmerField[Int, ODSkimmerInternal]("observations", 0, 50, skims.values, x => x.observations) checkSkimmerField[Int, ODSkimmerInternal]("iterations", 2, 5, skims.values, x => x.iterations) checkSkimmerField[Double, ODSkimmerInternal]( diff --git a/src/test/scala/beam/router/skim/SkimmerSpec.scala b/src/test/scala/beam/router/skim/SkimmerSpec.scala index 16eea37bd8e..d477f7b5136 100644 --- a/src/test/scala/beam/router/skim/SkimmerSpec.scala +++ b/src/test/scala/beam/router/skim/SkimmerSpec.scala @@ -278,7 +278,7 @@ object SkimmerSpec extends LazyLogging { energy = Option(row("energy")).map(_.toDouble).getOrElse(0.0), level4CavTravelTimeScalingFactor = Option(row("level4CavTravelTimeScalingFactor")).map(_.toDouble).getOrElse(1.0), - failedTrips = row.get("failedTrips").map(_.toDouble).getOrElse(0.0), + failedTrips = row.get("failedTrips").map(_.toInt).getOrElse(0), observations = row("observations").toInt, iterations = row("iterations").toInt ) diff --git a/test/test-resources/beam/router/skim/skims.csv b/test/test-resources/beam/router/skim/skims.csv index 088df7c7e89..28d989834db 100644 --- a/test/test-resources/beam/router/skim/skims.csv +++ b/test/test-resources/beam/router/skim/skims.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c21066d10eb8c33a71bc11e3c557b1549fe7c13f2b3c0fb6150f79a15a065587 -size 422 +oid sha256:6acbdc8550caba1c00004e6bd825aa91c20878e100ea7437761093734a7b42cf +size 442