-
Notifications
You must be signed in to change notification settings - Fork 1k
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
base: dev-2.x
Are you sure you want to change the base?
Take rental vehicle battery level into account #5960
Conversation
Coordinates of a Scooter with currentRangeMeters (battery) = 0.0
758ffe8
to
f1e354b
Compare
|
||
public class BatteryValidator { | ||
|
||
public static boolean wouldBatteryRunOut(Object current) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an OptionalDouble
.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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."); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
if (vertex.getStation() instanceof VehicleRentalVehicle) { | ||
return ((VehicleRentalVehicle) vertex.getStation()).getCurrentRangeMeters(); | ||
} | ||
return Optional.empty(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
Codecov ReportAttention: Patch coverage is
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. |
Also please add tests for the edges that you change. Take a look at how it's done in |
fe612b8
to
6cd22f6
Compare
We have added to the StateEditorTest and created a new StateEdgeTest, as we only change the state within an edge |
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; | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There is no point in reviewing this before we agree on the algorithm/analysis/design - se discussion in issue #5959. |
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 VehicleRentalEdgealso added fields to State