Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take rental vehicle battery level into account #5960

Open
wants to merge 7 commits into
base: dev-2.x
Choose a base branch
from

Conversation

UserX20
Copy link

@UserX20 UserX20 commented Jul 9, 2024

Summary

If a vehicle, for example an E-scooter, was to be rented it was possible for this vehicle to have had not enough remaining power to complete the entire trip.
In this case you would probably want to use a different scooter, which has enough remaining energy.
This PR adds this feature.

Added a Skip Edge Strategy which we call in the A-Star Routing

Issue

#5959

Unit tests

added a unit test for BatteryDistanceSkipEdgeStrategy

Documentation

N/A

Bumping the serialization version id

We're unsure if it needs to be bumped. added private final VehicleRentalPlaceVertex vertex; to VehicleRentalEdge
also added fields to State

@UserX20 UserX20 requested a review from a team as a code owner July 9, 2024 14:55
@UserX20 UserX20 force-pushed the feat/max-battery-distance-rented-vehicles branch from 758ffe8 to f1e354b Compare July 10, 2024 11:06
@leonardehrenfried leonardehrenfried changed the title Feat/max battery distance rented vehicles Take battery level into account for rental vehicles Jul 11, 2024

public class BatteryValidator {

public static boolean wouldBatteryRunOut(Object current) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way you can get away without the cast?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not find any way, without breaking the AStarArchitectureTest.
If I got it right, we would have to use a concrete State as Generic in our Strategy, which would break the AStarArchitectureTest.

new DurationSkipEdgeStrategy(
preferences.maxDirectDuration().valueOf(request.journey().direct().mode())
),
new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only need this heuristic for rental searches. Can you only add it if there mode contains rental?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added a method called getSkipEdgeStrategy where we check if the mode includes renting and call the BatterySkipEdgeStrategy if true

@@ -133,4 +134,8 @@ public VehicleRentalStationUris getRentalUris() {
public VehicleRentalSystem getVehicleRentalSystem() {
return system;
}

public Optional<Double> getCurrentRangeMeters() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use an OptionalDouble.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just used a basic double because of this change: #5960 (comment)

@@ -52,6 +53,12 @@ public class State implements AStarState<State, Edge, Vertex>, Cloneable {
// we should DEFINITELY rename this variable and the associated methods.
public double walkDistance;

// how far a sharing vehicle powered by battery has driven
public double drivenBatteryMeters;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since bikes and scooters are ridden, I would call this variable travelledBatteryMeters or traversedBatteryMeters (more correct).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we chose traversedBatteryMeters as a new name, thanks

public double drivenBatteryMeters;

//the available battery distance of a currently selected sharing vehicle
public Optional<Double> currentRangeMeters;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reasons that I don't quite understand it's totally unusual in Java to have instance fields that are Optionals. You have to use a regular double with a magic value for "no value".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably also better for performance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, would it be ok, to use Double(using null as "no value"), the wrapper class instead of the primitive double?
Because using a magic value for "no value" sounds slightly ugly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that it's ugly but we do that quite a bit in OTP to get the absolute best performance. This is a very hot code path.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please make as many fields as possible private.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please make as many fields as possible private.

sounds good to me. We weren't sure here because the other fields in this class were public. But if you don't see a problem here, we'll change the added field.

Copy link
Contributor

@tobsesHub tobsesHub Jul 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that it's ugly but we do that quite a bit in OTP to get the absolute best performance. This is a very hot code path.

Okay. What do you think would be a good value?
Zero would be really bad because if the battery was emty, we would ignore our logic and it would use that vehicle.
Would Double.MAX_VALUE or DOUBLE.MIN_VALUE be a good value?
Or maybe a negative number, then we would have to change the check with the negative number.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We made it a basic double and used Double.POSITIVE_INFINITY as the initializing value

@@ -196,6 +197,19 @@ public void incrementWalkDistance(double length) {
child.walkDistance += length;
}

public void incrementDrivenBatteryMeters(double length) {
if (length < 0) {
LOG.warn("A state's battery distance is being incremented by a negative amount.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will have to check with the other developers but I would be totally unforgiving and throw an exception in this case. This indicates a bug elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The incrementWalkDistance, incrementTimeInSeconds, incrementWeight methods in the StateEditor class do it the same way(thwowing no exception. So probably, if we change this, we should change those methods too.

We could add exceptions like NegativeBatteryMeterIncrement, NegativeWalkDistanceIncrement...

But we don' t know really good the exception handling in OTP.
So at least, I would prefer to create a new PR for that, if we should change that.

Comment on lines 216 to 219
if (vertex.getStation() instanceof VehicleRentalVehicle) {
return ((VehicleRentalVehicle) vertex.getStation()).getCurrentRangeMeters();
}
return Optional.empty();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this into the domain model, ie. have a method on VehicleRentalPlace that encapsulates this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added it to VehicleRentalPlace and were able to remove the if-check as we have implementations in each of the specific Edge-Types

@@ -1183,6 +1183,10 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk
s1.incrementWalkDistance(getDistanceWithElevation());
}

if (traverseMode.isCyclingIsh() && s1.isVehicleRentable()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only for cyling-ish? Cars have a range as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the check for cycling-ish

@@ -389,4 +403,8 @@ private void cloneStateDataAsNeeded() {
if (child.backState != null && child.stateData == child.backState.stateData) child.stateData =
child.stateData.clone();
}

public boolean isVehicleRentable() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this have a different name from the delegate method?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We changed it to isRentableVehicle, as the delegate is called

@leonardehrenfried leonardehrenfried changed the title Take battery level into account for rental vehicles Take rental vehicle battery level into account for rental Jul 11, 2024
@leonardehrenfried leonardehrenfried changed the title Take rental vehicle battery level into account for rental Take rental vehicle battery level into account Jul 11, 2024
Copy link

codecov bot commented Jul 11, 2024

Codecov Report

Attention: Patch coverage is 76.31579% with 9 lines in your changes missing coverage. Please review.

Project coverage is 69.53%. Comparing base (20fc8ce) to head (f1e354b).
Report is 27 commits behind head on dev-2.x.

Files Patch % Lines
...org/opentripplanner/street/search/state/State.java 60.00% 2 Missing and 2 partials ⚠️
...entripplanner/street/search/state/StateEditor.java 55.55% 3 Missing and 1 partial ⚠️
...opentripplanner/routing/impl/BatteryValidator.java 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             dev-2.x    #5960   +/-   ##
==========================================
  Coverage      69.53%   69.53%           
- Complexity     17111    17129   +18     
==========================================
  Files           1938     1940    +2     
  Lines          73774    73810   +36     
  Branches        7548     7551    +3     
==========================================
+ Hits           51296    51327   +31     
- Misses         19840    19844    +4     
- Partials        2638     2639    +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@leonardehrenfried
Copy link
Member

Also please add tests for the edges that you change. Take a look at how it's done in StreetEdgeGeofencingTest.

@t2gran t2gran added this to the 2.6 (next release) milestone Jul 23, 2024
@UserX20 UserX20 force-pushed the feat/max-battery-distance-rented-vehicles branch from fe612b8 to 6cd22f6 Compare July 31, 2024 15:14
@UserX20
Copy link
Author

UserX20 commented Jul 31, 2024

Also please add tests for the edges that you change. Take a look at how it's done in StreetEdgeGeofencingTest.

We have added to the StateEditorTest and created a new StateEdgeTest, as we only change the state within an edge

@t2gran
Copy link
Member

t2gran commented Aug 12, 2024

Sorry, I would like to see a suggestion for the strategy on how to implement this, before starting to look at the code. I made a comment to the issue for ideas on how to implement it.

//the available battery distance of a currently selected sharing vehicle
//setting a magic value of 0.0 to make sure
public double currentRangeMeters;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said in the developer meeting, I agree that the algorithm itself has to be evaluated firstmost.

With that said, you can save a considerable amount of memory by selecting different data types for the fields.
By decreasing the granularity with three bits (8 meters) you can represent values up to 2040 meters using a single byte. The values are still comparable, it's just that the unit is octa-meters.

The value has to be mangled (shifted and casted) to fit but those operations are basically free from a CPU perspective.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice it is not so easy, but lets not go into discussion on HW and Java architecture before we know how to solve this. I have a strong feeling that we do not need to add anything to the state to solve this.

@t2gran
Copy link
Member

t2gran commented Aug 28, 2024

There is no point in reviewing this before we agree on the algorithm/analysis/design - se discussion in issue #5959.

@t2gran t2gran modified the milestones: 2.6, 2.7 (next release) Sep 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants