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

core: fix allowance space search discontinuities #5399

Merged
merged 4 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
package fr.sncf.osrd.envelope;

import fr.sncf.osrd.envelope.part.EnvelopePartBuilder;
import fr.sncf.osrd.envelope_sim.EnvelopeProfile;
import fr.sncf.osrd.envelope_utils.CmpOperator;
import java.util.Collection;
import java.util.HashSet;

public class EnvelopeSpeedCap {
/** Adds a global speed limit to an envelope */
public static Envelope from(
Envelope base,
Iterable<EnvelopeAttr> attrs,
Collection<EnvelopeAttr> attrs,
double speedLimit
) {
var cursor = new EnvelopeCursor(base, false);
var builder = OverlayEnvelopeBuilder.forward(base);
var envelopeAttrs = new HashSet<>(attrs);
envelopeAttrs.add(EnvelopeProfile.CONSTANT_SPEED);
while (cursor.findSpeed(speedLimit, CmpOperator.STRICTLY_HIGHER)) {
var startPos = cursor.getPosition();

var partBuilder = new EnvelopePartBuilder();
partBuilder.setAttrs(attrs);
partBuilder.setAttrs(envelopeAttrs);
partBuilder.initEnvelopePart(startPos, speedLimit, 1);

var hasNotReachedEnd = cursor.findSpeed(speedLimit, CmpOperator.STRICTLY_LOWER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import fr.sncf.osrd.envelope.EnvelopeAttr;
import fr.sncf.osrd.envelope.EnvelopePhysics;
import fr.sncf.osrd.envelope.SearchableEnvelope;
import fr.sncf.osrd.envelope_sim.EnvelopeProfile;
import fr.sncf.osrd.envelope_utils.ExcludeFromGeneratedCodeCoverage;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -106,19 +107,6 @@ public static EnvelopePart generateTimes(
);
}

/** Creates an envelope part by generating step times from speeds and positions */
public static EnvelopePart generateTimes(
double[] positions,
double[] speeds
) {
return new EnvelopePart(
new HashMap<>(),
positions,
speeds,
computeTimes(positions, speeds)
);
}

// endregion

// region ATTRS
Expand Down Expand Up @@ -165,6 +153,7 @@ public Map<Class<? extends EnvelopeAttr>, EnvelopeAttr> getAttrs() {
* (which should be avoided when possible) */
private void runSanityChecks() {
assert attrs != null : "missing attributes";
assert hasAttr(EnvelopeProfile.class) : "missing EnvelopeProfile attribute";
assert positions.length > 0 : "attempted to create an empty EnvelopePart";
assert positions.length == speeds.length : "there must be the same number of point and speeds";
assert timeDeltas.length == positions.length - 1 : "there must be as many timeDeltas as gaps between points";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static fr.sncf.osrd.envelope.EnvelopeCursor.NextStepResult.NEXT_PART;
import static fr.sncf.osrd.envelope.EnvelopeCursor.NextStepResult.NEXT_REACHED_END;
import static fr.sncf.osrd.envelope.part.constraints.EnvelopePartConstraintType.*;
import static fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator.areSpeedsEqual;

import fr.sncf.osrd.envelope.Envelope;
import fr.sncf.osrd.envelope.EnvelopeCursor;
Expand Down Expand Up @@ -32,9 +33,9 @@ public boolean initCheck(double position, double speed, double direction) {
var envelopeSpeed = part.interpolateSpeed(stepIndex, position);
cursor = new EnvelopeCursor(envelope, direction < 0, partIndex, stepIndex, position);
return switch (type) {
case CEILING -> envelopeSpeed >= speed;
case FLOOR -> envelopeSpeed <= speed;
case EQUAL -> envelopeSpeed == speed;
case CEILING -> envelopeSpeed > speed || areSpeedsEqual(envelopeSpeed, speed);
case FLOOR -> envelopeSpeed < speed || areSpeedsEqual(envelopeSpeed, speed);
case EQUAL -> areSpeedsEqual(envelopeSpeed, speed);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import fr.sncf.osrd.envelope.part.constraints.EnvelopeConstraint;
import fr.sncf.osrd.envelope.part.constraints.EnvelopePartConstraint;
import fr.sncf.osrd.envelope.part.constraints.PositionConstraint;
import fr.sncf.osrd.envelope_sim.EnvelopeProfile;
import fr.sncf.osrd.envelope_sim.EnvelopeSimContext;
import fr.sncf.osrd.envelope_sim.PhysicsRollingStock;
import fr.sncf.osrd.envelope_sim.allowances.utils.AllowanceRange;
Expand Down Expand Up @@ -157,7 +158,7 @@ private Envelope[] computeAllowanceRegion(Envelope envelopeRegion, EnvelopeSimCo
var range = ranges.get(i);
var percentage = range.value.getAllowanceRatio(
envelopeRegion.getTimeBetween(range.beginPos, range.endPos),
range.beginPos - range.endPos
range.endPos - range.beginPos
);
rangePercentages[i] = new RangePercentage(range, percentage);
}
Expand Down Expand Up @@ -401,6 +402,7 @@ private EnvelopePart computeLeftJunction(Envelope envelopeSection,
EnvelopeDeceleration.decelerate(
context, envelopeSection.getBeginPos(), imposedBeginSpeed, constrainedBuilder, 1
);
partBuilder.setAttr(EnvelopeProfile.BRAKING);
lastIntersection = constrainedBuilder.lastIntersection;
} else if (imposedBeginSpeed < envelopeSection.getBeginSpeed()) {
constraints.add(new EnvelopeConstraint(envelopeTarget, CEILING));
Expand All @@ -412,6 +414,7 @@ private EnvelopePart computeLeftJunction(Envelope envelopeSection,
EnvelopeAcceleration.accelerate(
context, envelopeSection.getBeginPos(), imposedBeginSpeed, constrainedBuilder, 1
);
partBuilder.setAttr(EnvelopeProfile.ACCELERATING);
lastIntersection = constrainedBuilder.lastIntersection;
}
if (lastIntersection == 0) {
Expand Down Expand Up @@ -449,6 +452,7 @@ private EnvelopePart computeRightJunction(Envelope envelopeSection,
EnvelopeAcceleration.accelerate(
context, envelopeSection.getEndPos(), imposedEndSpeed, constrainedBuilder, -1
);
partBuilder.setAttr(EnvelopeProfile.ACCELERATING);
lastIntersection = constrainedBuilder.lastIntersection;
} else if (imposedEndSpeed < envelopeSection.getEndSpeed()) {
constraints.add(new EnvelopeConstraint(envelopeTarget, CEILING));
Expand All @@ -459,6 +463,7 @@ private EnvelopePart computeRightJunction(Envelope envelopeSection,
EnvelopeDeceleration.decelerate(
context, envelopeSection.getEndPos(), imposedEndSpeed, constrainedBuilder, -1
);
partBuilder.setAttr(EnvelopeProfile.BRAKING);
lastIntersection = constrainedBuilder.lastIntersection;
}
if (lastIntersection == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static fr.sncf.osrd.envelope.part.constraints.EnvelopePartConstraintType.CEILING;
import static fr.sncf.osrd.envelope.part.constraints.EnvelopePartConstraintType.FLOOR;
import static fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator.areSpeedsEqual;

import fr.sncf.osrd.envelope.Envelope;
import fr.sncf.osrd.envelope.part.ConstrainedEnvelopePartBuilder;
Expand All @@ -11,15 +12,16 @@
import fr.sncf.osrd.envelope.part.constraints.SpeedConstraint;
import fr.sncf.osrd.envelope_sim.*;
import fr.sncf.osrd.envelope_sim.overlays.EnvelopeCoasting;
import fr.sncf.osrd.reporting.exceptions.OSRDError;
import fr.sncf.osrd.reporting.exceptions.ErrorType;
import fr.sncf.osrd.reporting.exceptions.OSRDError;

public final class CoastingGenerator {
/** Generate a coasting envelope part which starts at startPos */
public static EnvelopePart coastFromBeginning(
Envelope envelope,
EnvelopeSimContext context,
double startPos
double startPos,
double startSpeed
) {
var partBuilder = new EnvelopePartBuilder();
partBuilder.setAttr(EnvelopeProfile.COASTING);
Expand All @@ -28,8 +30,7 @@ public static EnvelopePart coastFromBeginning(
new SpeedConstraint(0, FLOOR),
new EnvelopeConstraint(envelope, CEILING)
);
var speed = envelope.interpolateSpeed(startPos);
EnvelopeCoasting.coast(context, startPos, speed, constrainedBuilder, 1);
EnvelopeCoasting.coast(context, startPos, startSpeed, constrainedBuilder, 1);
if (constrainedBuilder.lastIntersection == 0)
throw new OSRDError(ErrorType.ImpossibleSimulationError); // We reached a stop while coasting
if (partBuilder.isEmpty())
Expand All @@ -51,6 +52,7 @@ public static EnvelopePart coastFromEnd(

// coast backwards from the end position until the base curve is met
var backwardPartBuilder = new EnvelopePartBuilder();
backwardPartBuilder.setAttr(EnvelopeProfile.COASTING);
var constrainedBuilder = new ConstrainedEnvelopePartBuilder(
backwardPartBuilder,
new SpeedConstraint(0, FLOOR),
Expand All @@ -66,7 +68,7 @@ public static EnvelopePart coastFromEnd(
var step = TrainPhysicsIntegrator.step(context, position, speed, Action.COAST, -1);
position += step.positionDelta;
speed = step.endSpeed;
if (speed < lowSpeedLimit) {
if (!areSpeedsEqual(speed, lowSpeedLimit) && speed < lowSpeedLimit) {
speed = lowSpeedLimit;
reachedLowLimit = true;
}
Expand All @@ -83,7 +85,8 @@ public static EnvelopePart coastFromEnd(
if (!reachedLowLimit && constrainedBuilder.getLastPos() != envelope.getBeginPos())
return backwardPartBuilder.build();

var resultCoast = coastFromBeginning(envelope, context, constrainedBuilder.getLastPos());
var resultCoast = coastFromBeginning(envelope, context, constrainedBuilder.getLastPos(),
constrainedBuilder.getLastSpeed());
assert resultCoast == null || resultCoast.getEndPos() <= endPos + context.timeStep * speed;
return resultCoast;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import fr.sncf.osrd.envelope.EnvelopeTestUtils.TestAttr;
import fr.sncf.osrd.envelope.part.ConstrainedEnvelopePartBuilder;
import fr.sncf.osrd.envelope.part.EnvelopePart;
import fr.sncf.osrd.envelope.part.EnvelopePartBuilder;
import fr.sncf.osrd.envelope.part.EnvelopePartConsumer;
import fr.sncf.osrd.envelope.part.constraints.EnvelopeConstraint;
import fr.sncf.osrd.envelope.part.constraints.PositionConstraint;
import fr.sncf.osrd.envelope.part.constraints.SpeedConstraint;
import fr.sncf.osrd.envelope_sim.EnvelopeProfile;
import org.junit.jupiter.api.Test;
import java.util.List;

Expand All @@ -27,13 +27,11 @@ public class ConstraintBuilderTest {
// 0 1 2 3 4 5 6 7 8 9 10

private ConstrainedEnvelopePartBuilder wrap(EnvelopePartConsumer sink) {
var envelopeFloor = Envelope.make(EnvelopePart.generateTimes(
List.of(),
var envelopeFloor = Envelope.make(EnvelopeTestUtils.generateTimes(
new double[] {0, 3, 4, 5, 6, 7, 10},
new double[] {0, 0, 1, 2, 1, 0, 0}
));
var envelopeCeiling = Envelope.make(EnvelopePart.generateTimes(
List.of(),
var envelopeCeiling = Envelope.make(EnvelopeTestUtils.generateTimes(
new double[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
new double[] {2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2}
));
Expand All @@ -53,6 +51,7 @@ void testAttrs() {
var builder = wrap(partBuilder);
builder.setAttrs(List.of(TestAttr.A));
builder.setAttr(TestAttr.B);
builder.setAttr(EnvelopeProfile.ACCELERATING);
assertTrue(builder.initEnvelopePart(2, 0, 1));
assertFalse(builder.addStep(5, 1));
var part = partBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@

import static org.junit.jupiter.api.Assertions.assertTrue;

import fr.sncf.osrd.envelope.part.EnvelopePart;
import org.junit.jupiter.api.Test;

public class EnvelopeBuilderTest {
@Test
public void testEnvelopeBuilder() {
var builder = new EnvelopeBuilder();
builder.addPart(EnvelopePart.generateTimes(
builder.addPart(EnvelopeTestUtils.generateTimes(
new double[]{0, 1},
new double[]{10, 20}
));
builder.addPart(EnvelopePart.generateTimes(
builder.addPart(EnvelopeTestUtils.generateTimes(
new double[]{1, 2},
new double[]{20, 30}
));
Expand All @@ -24,11 +23,11 @@ public void testEnvelopeBuilder() {
@Test
public void testEnvelopeBuilderReversed() {
var builder = new EnvelopeBuilder();
builder.addPart(EnvelopePart.generateTimes(
builder.addPart(EnvelopeTestUtils.generateTimes(
new double[]{1, 2},
new double[]{20, 30}
));
builder.addPart(EnvelopePart.generateTimes(
builder.addPart(EnvelopeTestUtils.generateTimes(
new double[]{0, 1},
new double[]{10, 20}
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@

import static org.junit.jupiter.api.Assertions.*;

import fr.sncf.osrd.envelope.part.EnvelopePart;
import org.junit.jupiter.api.Test;


public class EnvelopeCursorTest {
public static final Envelope FLAT_ENVELOPE = Envelope.make(
EnvelopePart.generateTimes(
EnvelopeTestUtils.generateTimes(
new double[]{1, 3, 4},
new double[]{4, 4, 4}
),
EnvelopePart.generateTimes(
EnvelopeTestUtils.generateTimes(
new double[]{4, 6},
new double[]{4, 4}
),
EnvelopePart.generateTimes(
EnvelopeTestUtils.generateTimes(
new double[]{6, 8, 10},
new double[]{4, 4, 4}
)
Expand Down
Loading