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: only apply driver behavior to BAL and BAPR #10768

Merged
merged 2 commits into from
Feb 12, 2025
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package fr.sncf.osrd

import fr.sncf.osrd.envelope.Envelope
import fr.sncf.osrd.envelope.MRSPEnvelopeBuilder
import fr.sncf.osrd.envelope.part.EnvelopePart
import fr.sncf.osrd.envelope_sim.EnvelopeProfile
import fr.sncf.osrd.utils.DistanceRangeMap
import fr.sncf.osrd.utils.distanceRangeMapOf
import fr.sncf.osrd.utils.units.meters
import kotlin.math.max
import kotlin.math.min

data class DriverBehaviour(
val acceleratingPostponementOffset: Double = 50.0,
val brakingAnticipationOffset: Double = 100.0,
val signalingSystems: List<String> = listOf("BAL", "BAPR")
) {
/** Applies the driver behavior to the MRSP, adding reaction time for MRSP changes */
fun applyToMRSP(
mrsp: Envelope,
optSignalingSystemRanges: DistanceRangeMap<String>? = null
): Envelope {
val signalingSystemRanges = optSignalingSystemRanges ?: distanceRangeMapOf()
val builder = MRSPEnvelopeBuilder()
val totalLength = mrsp.totalDistance
for (part in mrsp) {
var begin = part.beginPos
var end = part.endPos
// compute driver behaviour offsets
if (signalingSystems.contains(signalingSystemRanges.get(begin.meters) ?: ""))
begin -= this.brakingAnticipationOffset
if (signalingSystems.contains(signalingSystemRanges.get(end.meters) ?: ""))
end += this.acceleratingPostponementOffset
begin = max(0.0, begin)
end = min(totalLength, end)
val speed = part.maxSpeed

builder.addPart(
EnvelopePart.generateTimes(
listOf(
EnvelopeProfile.CONSTANT_SPEED,
MRSPEnvelopeBuilder.LimitKind.SPEED_LIMIT
),
doubleArrayOf(begin, end),
doubleArrayOf(speed, speed)
)
)
}
return builder.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,16 @@ interface DistanceRangeSet : Iterable<DistanceRangeSet.RangeSetEntry> {
fun distanceRangeSetOf(): DistanceRangeSet {
return DistanceRangeSetImpl()
}

/**
* Create a range set from a range map, with values set where the predicate matches the map value.
*/
fun <T> DistanceRangeMap<T>.mapToRangeSet(f: (T) -> Boolean): DistanceRangeSet {
val res = distanceRangeSetOf()
for (entry in this) {
if (f(entry.value)) {
res.put(entry.lower, entry.upper)
}
}
return res
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static StandaloneSimResult run(
// MRSP & SpeedLimits
var mrsp = computeMRSP(trainPath, rollingStock, true, trainSchedule.tag, null, null, true);
var speedLimits = computeMRSP(trainPath, rollingStock, false, trainSchedule.tag, null, null, true);
mrsp = driverBehaviour.applyToMRSP(mrsp);
mrsp = driverBehaviour.applyToMRSP(mrsp, null);
cacheSpeedLimits.put(trainSchedule, ResultEnvelopePoint.from(speedLimits));

// Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import fr.sncf.osrd.standalone_sim.result.ElectrificationRange.ElectrificationUs
import fr.sncf.osrd.standalone_sim.result.ElectrificationRange.ElectrificationUsage.ElectrifiedUsage
import fr.sncf.osrd.train.RollingStock
import fr.sncf.osrd.utils.DistanceRangeMap
import fr.sncf.osrd.utils.distanceRangeMapOf
import fr.sncf.osrd.utils.indexing.StaticIdxList
import fr.sncf.osrd.utils.units.Distance
import fr.sncf.osrd.utils.units.Offset
Expand Down Expand Up @@ -70,6 +71,7 @@ fun runStandaloneSimulation(
driverBehaviour: DriverBehaviour = DriverBehaviour()
): SimulationSuccess {
if (chunkPath.length == 0.meters) throw OSRDError(ZeroLengthPath)
val signalingRanges = buildSignalingRanges(infra, blockPath, chunkPath)
// MRSP & SpeedLimits
val safetySpeedRanges = makeSafetySpeedRanges(infra, chunkPath, routes, schedule)
var mrsp =
Expand All @@ -82,7 +84,7 @@ fun runStandaloneSimulation(
safetySpeedRanges,
useInfraSpeedLimits
)
mrsp = driverBehaviour.applyToMRSP(mrsp)
mrsp = driverBehaviour.applyToMRSP(mrsp, signalingRanges)
// We don't use speed safety ranges in the MRSP displayed in the front
// (just like we don't add the train length)
val speedLimits =
Expand Down Expand Up @@ -115,7 +117,7 @@ fun runStandaloneSimulation(
envelopeSimPath,
timeStep,
curvesAndConditions.curves,
makeETCSContext(rollingStock, infra, chunkPath, routes, blockPath)
makeETCSContext(rollingStock, infra, chunkPath, routes, signalingRanges)
)

// Max speed envelope
Expand Down Expand Up @@ -190,6 +192,33 @@ fun runStandaloneSimulation(
)
}

/** Returns the ranges where each signaling system is encountered, as travelled path offsets. */
fun buildSignalingRanges(
infra: FullInfra,
blockPath: StaticIdxList<Block>,
chunkPath: ChunkPath
): DistanceRangeMap<String> {
val blockInfra = infra.blockInfra
var blockStartOffset =
Offset<TravelledPath>(
trainPathBlockOffset(infra.rawInfra, infra.blockInfra, blockPath, chunkPath) * -1.0
)
val res = distanceRangeMapOf<String>()
for (blockId in blockPath) {
val blockLength = blockInfra.getBlockLength(blockId)
val blockEndOffset = blockStartOffset + blockLength.distance
val sigSystem = blockInfra.getBlockSignalingSystem(blockId)
val name = infra.signalingSimulator.sigModuleManager.getName(sigSystem)
res.put(
Distance.max(blockStartOffset.distance, Distance.ZERO),
Distance.min(blockEndOffset.distance, chunkPath.length),
name,
)
blockStartOffset += blockLength.distance
}
return res
}

fun makeElectricalProfiles(
electrificationRanges: List<ElectrificationRange>
): RangeValues<ElectricalProfileValue> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import fr.sncf.osrd.sim_infra.impl.ChunkPath
import fr.sncf.osrd.sim_infra.utils.getNextTrackSections
import fr.sncf.osrd.train.RollingStock
import fr.sncf.osrd.utils.Direction
import fr.sncf.osrd.utils.distanceRangeSetOf
import fr.sncf.osrd.utils.DistanceRangeMap
import fr.sncf.osrd.utils.indexing.DirStaticIdx
import fr.sncf.osrd.utils.indexing.StaticIdxList
import fr.sncf.osrd.utils.units.Distance
import fr.sncf.osrd.utils.mapToRangeSet
import fr.sncf.osrd.utils.units.Offset

/** Build the ETCS context, if relevant. */
Expand All @@ -20,31 +20,9 @@ fun makeETCSContext(
infra: FullInfra,
chunkPath: ChunkPath,
routePath: StaticIdxList<Route>,
blockPath: StaticIdxList<Block>
signalingRanges: DistanceRangeMap<String>,
): EnvelopeSimContext.ETCSContext? {
val blockInfra = infra.blockInfra
val etcsRanges = distanceRangeSetOf()
val etcsLevel2 =
infra.signalingSimulator.sigModuleManager.findSignalingSystemOrThrow(ETCS_LEVEL2.id)
var blockStartOffset =
Offset<TravelledPath>(
trainPathBlockOffset(infra.rawInfra, infra.blockInfra, blockPath, chunkPath) * -1.0
)
for (blockId in blockPath) {
val blockLength = blockInfra.getBlockLength(blockId)
val blockEndOffset = blockStartOffset + blockLength.distance
if (
blockInfra.getBlockSignalingSystem(blockId) == etcsLevel2 &&
chunkPath.length >= blockStartOffset.distance &&
blockEndOffset.distance >= Distance.ZERO
) {
etcsRanges.put(
Distance.max(blockStartOffset.distance, Distance.ZERO),
Distance.min(blockEndOffset.distance, chunkPath.length)
)
}
blockStartOffset += blockLength.distance
}
val etcsRanges = signalingRanges.mapToRangeSet { it == ETCS_LEVEL2.id }

if (etcsRanges.asList().isEmpty()) {
return null
Expand Down
10 changes: 9 additions & 1 deletion core/src/test/java/fr/sncf/osrd/DriverBehaviourTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import fr.sncf.osrd.api.pathfinding.makePathProps
import fr.sncf.osrd.envelope_sim_infra.computeMRSP
import fr.sncf.osrd.sim_infra.api.PathProperties
import fr.sncf.osrd.train.TestTrains
import fr.sncf.osrd.utils.DistanceRangeMap
import fr.sncf.osrd.utils.DummyInfra
import fr.sncf.osrd.utils.distanceRangeMapOf
import fr.sncf.osrd.utils.units.Length
import fr.sncf.osrd.utils.units.meters
import org.junit.jupiter.api.Assertions
Expand All @@ -24,7 +26,13 @@ class DriverBehaviourTest {
val testRollingStock = TestTrains.VERY_SHORT_FAST_TRAIN
val driverBehaviour = DriverBehaviour(2.0, 3.0)
var mrsp = computeMRSP(path, testRollingStock, true, null, null)
mrsp = driverBehaviour.applyToMRSP(mrsp)
mrsp =
driverBehaviour.applyToMRSP(
mrsp,
distanceRangeMapOf(
DistanceRangeMap.RangeMapEntry(0.meters, path.getLength(), "BAL")
)
)
Assertions.assertEquals(20.0, mrsp.interpolateSpeedRightDir(0.0, 1.0))
Assertions.assertEquals(10.0, mrsp.interpolateSpeedRightDir((100 - 3).toDouble(), 1.0))
Assertions.assertEquals(
Expand Down
Loading