Skip to content

Commit 54ae866

Browse files
Erashineckter
authored andcommitted
core: migrate stdcm to new infra
Change summary: * Migrate train simulation code to work with the new infra interfaces * Migrate STDCM to work with the new infra interfaces * Run the STDCM pathfinding on blocks instead of routes * Migrate tests, using a new class to build simplified infras * Cleanup some legacy code (not all of it)
1 parent 5a73255 commit 54ae866

File tree

121 files changed

+3904
-4472
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+3904
-4472
lines changed

core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/EnvelopeSimContext.java

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public EnvelopeSimContext(
1818
this.rollingStock = rollingStock;
1919
this.path = path;
2020
this.timeStep = timeStep;
21+
assert tractiveEffortCurveMap != null;
2122
this.tractiveEffortCurveMap = tractiveEffortCurveMap;
2223
}
2324

core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/TrainPhysicsIntegrator.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
* the next position and speed of the train.
1010
*/
1111
public final class TrainPhysicsIntegrator {
12-
// a position delta lower than this value will be considered zero
13-
public static final double POSITION_EPSILON = 1E-6;
14-
// a speed lower than this value will be considered zero
12+
// A position delta lower than this value will be considered zero
13+
// Going back and forth with Distance and double (meters) often causes 1e-3 errors,
14+
// we need the tolerance to be higher than this
15+
public static final double POSITION_EPSILON = 1E-2;
16+
// A speed lower than this value will be considered zero
1517
public static final double SPEED_EPSILON = 1E-6;
1618

1719
private final PhysicsRollingStock rollingStock;

core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_utils/RangeMapUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static <T> RangeMap<Double, T> mergeRanges(RangeMap<Double, T> map) {
3434
}
3535

3636
/**
37-
* Return the first map updated with another, using a merge function to fuse the values of intersecting ranges
37+
* Returns the first map updated with another, using a merge function to fuse the values of intersecting ranges
3838
*/
3939
public static <T, U> TreeRangeMap<Double, T> updateRangeMap(RangeMap<Double, T> map, RangeMap<Double, U> update,
4040
BiFunction<T, U, T> mergeFunction) {

core/kt-osrd-sim-infra/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencies {
1818
// PLEASE ADD AND UPDATE DEPENDENCIES USING libs.versions.toml
1919
implementation project(':kt-fast-collections')
2020
implementation project(path: ':osrd-geom')
21+
implementation project(path: ':envelope-sim')
2122
ksp project(':kt-fast-collections-generator')
2223

2324
api project(":kt-osrd-utils")

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/InterlockingInfra.kt

+6
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,22 @@ interface RoutingInfra : ReservationInfra {
8585
fun getChunksOnRoute(route: RouteId): DirStaticIdxList<TrackChunk>
8686
@JvmName("getRoutesOnTrackChunk")
8787
fun getRoutesOnTrackChunk(trackChunk: DirTrackChunkId): StaticIdxList<Route>
88+
@JvmName("getRoutesStartingAtDet")
89+
fun getRoutesStartingAtDet(dirDetector: DirDetectorId): StaticIdxList<Route>
90+
@JvmName("getRoutesEndingAtDet")
91+
fun getRoutesEndingAtDet(dirDetector: DirDetectorId): StaticIdxList<Route>
8892
}
8993

9094
fun ReservationInfra.findZonePath(entry: DirDetectorId, exit: DirDetectorId): ZonePathId? {
9195
return findZonePath(entry, exit, mutableStaticIdxArrayListOf(), mutableStaticIdxArrayListOf())
9296
}
9397

98+
@JvmName("getRouteEntry")
9499
fun RoutingInfra.getRouteEntry(route: RouteId): DirDetectorId {
95100
return getZonePathEntry(getRoutePath(route).first())
96101
}
97102

103+
@JvmName("getRouteExit")
98104
fun RoutingInfra.getRouteExit(route: RouteId): DirDetectorId {
99105
return getZonePathExit(getRoutePath(route).last())
100106
}

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/PathProperties.kt

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package fr.sncf.osrd.sim_infra.api
22

33
import fr.sncf.osrd.geom.LineString
4+
import fr.sncf.osrd.reporting.exceptions.ErrorType
5+
import fr.sncf.osrd.reporting.exceptions.OSRDError
46
import fr.sncf.osrd.sim_infra.impl.NeutralSection
57
import fr.sncf.osrd.sim_infra.impl.PathPropertiesImpl
8+
import fr.sncf.osrd.utils.DistanceRangeMap
69
import fr.sncf.osrd.utils.indexing.DirStaticIdxList
710
import fr.sncf.osrd.utils.indexing.StaticIdx
8-
import fr.sncf.osrd.utils.units.Distance
9-
import fr.sncf.osrd.utils.DistanceRangeMap
1011
import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf
12+
import fr.sncf.osrd.utils.units.Distance
13+
import fr.sncf.osrd.utils.units.Offset
14+
import fr.sncf.osrd.utils.units.Speed
1115
import fr.sncf.osrd.utils.units.meters
1216

1317
data class IdxWithOffset<T>(
@@ -21,7 +25,7 @@ data class TrackLocation(
2125
@get:JvmName("getTrackId")
2226
val trackId: TrackSectionId,
2327
@get:JvmName("getOffset")
24-
val offset: Distance
28+
val offset: Offset<TrackSection>
2529
)
2630

2731
@Suppress("INAPPLICABLE_JVM_NAME")
@@ -36,11 +40,27 @@ interface PathProperties {
3640
fun getCatenary(): DistanceRangeMap<String>
3741
@JvmName("getNeutralSections")
3842
fun getNeutralSections(): DistanceRangeMap<NeutralSection>
43+
@JvmName("getSpeedLimits")
44+
fun getSpeedLimits(trainTag: String?): DistanceRangeMap<Speed>
3945
@JvmName("getLength")
4046
fun getLength(): Distance
41-
4247
@JvmName("getTrackLocationAtOffset")
4348
fun getTrackLocationAtOffset(pathOffset: Distance): TrackLocation
49+
fun getElectricalProfiles(mapping: HashMap<String, DistanceRangeMap<String>>): DistanceRangeMap<String>
50+
fun getTrackLocationOffset(location: TrackLocation): Distance?
51+
52+
val chunks: DirStaticIdxList<TrackChunk>
53+
/** Returns the offset where the train starts (must be located on the first chunk) */
54+
val beginOffset: Distance
55+
}
56+
57+
/** Wraps the method without returning an optional, which can't be handled in java for value types */
58+
@JvmName("getTrackLocationOffsetOrThrow")
59+
fun PathProperties.getTrackLocationOffsetOrThrow(location: TrackLocation): Distance {
60+
val res = getTrackLocationOffset(location)
61+
if (res != null)
62+
return res
63+
throw OSRDError(ErrorType.InvalidScheduleTrackLocationNotIncludedInPath)
4464
}
4565

4666
/** Build a Path from chunks and offsets, filtering the chunks outside the offsets */
@@ -73,3 +93,9 @@ fun buildPathPropertiesFrom(
7393
}
7494
return PathPropertiesImpl(infra, filteredChunks, mutBeginOffset, mutEndOffset)
7595
}
96+
97+
/** For java interoperability purpose */
98+
@JvmName("makeTrackLocation")
99+
fun makeTrackLocation(track: TrackSectionId, offset: Offset<TrackSection>): TrackLocation {
100+
return TrackLocation(track, offset)
101+
}

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/RawSignalingInfra.kt

+10
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,14 @@ fun RawSignalingInfra.getLogicalSignalName(signal: LogicalSignalId): String? {
4444
return getPhysicalSignalName(getPhysicalSignal(signal))
4545
}
4646

47+
/** Returns the length of a route */
48+
@JvmName("getRouteLength")
49+
fun RawSignalingInfra.getRouteLength(route: RouteId): Distance {
50+
var res = 0.meters
51+
for (zonePath in getRoutePath(route)) {
52+
res += getZonePathLength(zonePath).distance
53+
}
54+
return res
55+
}
56+
4757
typealias RawInfra = RawSignalingInfra

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/api/TrackProperties.kt

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ interface TrackProperties {
3939
fun getTrackChunkSpeedSections(trackChunk: DirTrackChunkId, trainTag: String?): DistanceRangeMap<Speed>
4040
@JvmName("getTrackChunkGeom")
4141
fun getTrackChunkGeom(trackChunk: TrackChunkId): LineString
42+
fun getTrackChunkElectricalProfile(
43+
trackChunk: TrackChunkId, mapping: HashMap<String, DistanceRangeMap<String>>
44+
): DistanceRangeMap<String>
4245

4346
// Operational points
4447
fun getTrackChunkOperationalPointParts(trackChunk: TrackChunkId): StaticIdxList<OperationalPointPart>

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/BlockInfraImpl.kt

+20-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package fr.sncf.osrd.sim_infra.impl
33
import fr.sncf.osrd.sim_infra.api.*
44
import fr.sncf.osrd.utils.Direction
55
import fr.sncf.osrd.utils.indexing.*
6-
import fr.sncf.osrd.utils.units.*
6+
import fr.sncf.osrd.utils.units.Length
7+
import fr.sncf.osrd.utils.units.OffsetList
78

89
class BlockDescriptor(
910
val length: Length<Block>,
@@ -121,3 +122,21 @@ class BlockInfraImpl(
121122
return blockPool[block].length
122123
}
123124
}
125+
126+
@JvmName("getBlockEntry")
127+
fun BlockInfra.getRouteEntry(rawInfra: RawInfra, block: BlockId): DirDetectorId {
128+
val blockPath: StaticIdxList<ZonePath> = getBlockPath(block)
129+
val firstZone: ZonePathId = blockPath[0]
130+
return rawInfra.getZonePathEntry(firstZone)
131+
}
132+
133+
@JvmName("getBlockExit")
134+
fun BlockInfra.getRouteExit(rawInfra: RawInfra, block: BlockId): DirDetectorId {
135+
val blockPath: StaticIdxList<ZonePath> = getBlockPath(block)
136+
val lastZonePath: ZonePathId = blockPath[blockPath.size - 1]
137+
return rawInfra.getZonePathExit(lastZonePath)
138+
}
139+
140+
fun BlockInfra.getTrackChunksFromBlocks(blocks: List<BlockId>): List<DirStaticIdx<TrackChunk>> {
141+
return blocks.flatMap { getTrackChunksFromBlock(it) }
142+
}

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/DebugViewers.kt

+21-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import fr.sncf.osrd.sim_infra.api.*
44
import fr.sncf.osrd.sim_infra.utils.recoverBlocks
55
import fr.sncf.osrd.sim_infra.utils.toList
66
import fr.sncf.osrd.utils.Direction
7+
import fr.sncf.osrd.utils.indexing.DirStaticIdx
78
import fr.sncf.osrd.utils.indexing.MutableStaticIdxArrayList
89
import fr.sncf.osrd.utils.indexing.StaticIdx
9-
import fr.sncf.osrd.utils.indexing.StaticIdxList
1010
import fr.sncf.osrd.utils.units.Distance
1111
import fr.sncf.osrd.utils.units.Length
1212
import fr.sncf.osrd.utils.units.Offset
@@ -15,6 +15,7 @@ import fr.sncf.osrd.utils.units.Offset
1515
* when using a debugger. They can be used to create watches containing the object properties. */
1616

1717
data class DirectedViewer<T>(
18+
val rawId: UInt,
1819
val direction: Direction,
1920
val value: T,
2021
)
@@ -34,6 +35,9 @@ data class ZonePathViewer(
3435
data class BlockViewer(
3536
val zones: List<ZonePathViewer>,
3637
val id: BlockId,
38+
val entry: DirectedViewer<String>,
39+
val exit: DirectedViewer<String>,
40+
val length: Length<Block>,
3741
)
3842

3943
data class RouteViewer(
@@ -42,6 +46,10 @@ data class RouteViewer(
4246
val id: RouteId,
4347
)
4448

49+
fun <T, U> makeDirViewer(id: DirStaticIdx<T>, value: U): DirectedViewer<U> {
50+
return DirectedViewer(id.data, id.direction, value)
51+
}
52+
4553
@JvmName("makeChunk")
4654
fun makeChunk(infra: RawInfra, id: StaticIdx<TrackChunk>): ChunkViewer {
4755
return ChunkViewer(
@@ -52,36 +60,45 @@ fun makeChunk(infra: RawInfra, id: StaticIdx<TrackChunk>): ChunkViewer {
5260
)
5361
}
5462

63+
@JvmName("makeDirChunk")
64+
fun makeDirChunk(infra: RawInfra, id: DirStaticIdx<TrackChunk>): DirectedViewer<ChunkViewer> {
65+
return makeDirViewer(id, makeChunk(infra, id.value))
66+
}
67+
5568
@JvmName("makeZonePath")
5669
fun makeZonePath(infra: RawInfra, id: StaticIdx<ZonePath>): ZonePathViewer {
5770
return ZonePathViewer(
5871
infra.getZonePathChunks(id)
59-
.map { dirChunk -> DirectedViewer(dirChunk.direction, makeChunk(infra, dirChunk.value)) },
72+
.map { dirChunk -> makeDirViewer(dirChunk, makeChunk(infra, dirChunk.value)) },
6073
id,
6174
)
6275
}
6376

6477
@JvmName("makeBlock")
6578
fun makeBlock(rawInfra: RawInfra, blockInfra: BlockInfra, id: StaticIdx<Block>): BlockViewer {
79+
val entry = rawInfra.getZonePathEntry(blockInfra.getBlockPath(id)[0])
80+
val exit = rawInfra.getZonePathExit(blockInfra.getBlockPath(id).last())
6681
return BlockViewer(
6782
blockInfra.getBlockPath(id)
6883
.map { path -> makeZonePath(rawInfra, path) },
6984
id,
85+
makeDirViewer(entry, rawInfra.getDetectorName(entry.value)!!),
86+
makeDirViewer(exit, rawInfra.getDetectorName(exit.value)!!),
87+
blockInfra.getBlockLength(id),
7088
)
7189
}
7290

7391
@JvmName("makeRoute")
7492
fun makeRoute(
7593
rawInfra: RawInfra,
7694
blockInfra: BlockInfra,
77-
signalingSystems: StaticIdxList<SignalingSystem>,
7895
id: StaticIdx<Route>,
7996
): RouteViewer {
8097
val ids = MutableStaticIdxArrayList<Route>()
8198
ids.add(id)
8299
return RouteViewer(
83100
rawInfra.getRouteName(id)!!,
84-
recoverBlocks(rawInfra, blockInfra, ids, signalingSystems)[0]
101+
recoverBlocks(rawInfra, blockInfra, ids, null)[0]
85102
.toList()
86103
.map { blockPathElement -> blockPathElement.block }
87104
.map { block -> makeBlock(rawInfra, blockInfra, block) },

core/kt-osrd-sim-infra/src/main/kotlin/fr/sncf/osrd/sim_infra/impl/PathPropertiesImpl.kt

+64-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package fr.sncf.osrd.sim_infra.impl
22

33
import fr.sncf.osrd.geom.LineString
4-
import fr.sncf.osrd.sim_infra.api.*
4+
import fr.sncf.osrd.sim_infra.api.DirTrackChunkId
5+
import fr.sncf.osrd.sim_infra.api.IdxWithOffset
6+
import fr.sncf.osrd.sim_infra.api.LoadingGaugeConstraint
7+
import fr.sncf.osrd.sim_infra.api.OperationalPointPart
8+
import fr.sncf.osrd.sim_infra.api.PathProperties
9+
import fr.sncf.osrd.sim_infra.api.TrackChunk
10+
import fr.sncf.osrd.sim_infra.api.TrackChunkId
11+
import fr.sncf.osrd.sim_infra.api.TrackLocation
12+
import fr.sncf.osrd.sim_infra.api.TrackProperties
513
import fr.sncf.osrd.utils.Direction
614
import fr.sncf.osrd.utils.DistanceRangeMap
715
import fr.sncf.osrd.utils.distanceRangeMapOf
816
import fr.sncf.osrd.utils.indexing.DirStaticIdxList
917
import fr.sncf.osrd.utils.units.Distance
18+
import fr.sncf.osrd.utils.units.Offset
19+
import fr.sncf.osrd.utils.units.Speed
1020
import fr.sncf.osrd.utils.units.meters
11-
import java.lang.RuntimeException
1221

1322
data class PathPropertiesImpl(
1423
val infra: TrackProperties,
15-
val chunks: DirStaticIdxList<TrackChunk>,
24+
override val chunks: DirStaticIdxList<TrackChunk>,
1625
@get:JvmName("getBeginOffset")
17-
val beginOffset: Distance,
26+
override val beginOffset: Distance,
1827
@get:JvmName("getEndOffset")
1928
val endOffset: Distance,
2029
) : PathProperties {
@@ -53,6 +62,10 @@ data class PathPropertiesImpl(
5362
return getRangeMap { dirChunkId -> infra.getTrackChunkNeutralSections(dirChunkId) }
5463
}
5564

65+
override fun getSpeedLimits(trainTag: String?): DistanceRangeMap<Speed> {
66+
return getRangeMap { dirChunkId -> infra.getTrackChunkSpeedSections(dirChunkId, trainTag) }
67+
}
68+
5669
override fun getLength(): Distance {
5770
return endOffset - beginOffset
5871
}
@@ -67,15 +80,26 @@ data class PathPropertiesImpl(
6780
val startChunkOffset = infra.getTrackChunkOffset(chunk.value).distance
6881
val offsetOnChunk = offset - lengthPrevChunks
6982
return if (chunk.direction == Direction.INCREASING)
70-
TrackLocation(trackId, offsetOnChunk + startChunkOffset)
83+
TrackLocation(trackId, Offset(offsetOnChunk + startChunkOffset))
7184
else
72-
TrackLocation(trackId, startChunkOffset + chunkLength - offsetOnChunk)
85+
TrackLocation(trackId, Offset(startChunkOffset + chunkLength - offsetOnChunk))
7386
}
7487
lengthPrevChunks += chunkLength
7588
}
7689
throw RuntimeException("The given path offset is larger than the path length")
7790
}
7891

92+
override fun getElectricalProfiles(mapping: HashMap<String, DistanceRangeMap<String>>): DistanceRangeMap<String> {
93+
return getRangeMapFromUndirected { chunkId -> infra.getTrackChunkElectricalProfile(chunkId, mapping) }
94+
}
95+
96+
override fun getTrackLocationOffset(location: TrackLocation): Distance? {
97+
val offset = getOffsetOfTrackLocationOnChunks(infra, location, chunks) ?: return null
98+
if (offset < beginOffset || offset > endOffset)
99+
return null
100+
return offset - beginOffset
101+
}
102+
79103
private fun projectLineString(getData: (chunkId: TrackChunkId) -> LineString): LineString {
80104
fun getDirData(dirChunkId: DirTrackChunkId): LineString {
81105
val data = getData(dirChunkId.value)
@@ -201,3 +225,37 @@ data class PathPropertiesImpl(
201225
return res
202226
}
203227
}
228+
229+
/** Returns the offset of a location on a given list of chunks */
230+
fun getOffsetOfTrackLocationOnChunks(
231+
infra: TrackProperties,
232+
location: TrackLocation,
233+
chunks: DirStaticIdxList<TrackChunk>,
234+
): Distance? {
235+
var offsetAfterFirstChunk = 0.meters
236+
for (dirChunk in chunks) {
237+
val chunkLength = infra.getTrackChunkLength(dirChunk.value)
238+
if (location.trackId == infra.getTrackFromChunk(dirChunk.value)) {
239+
val chunkOffset = infra.getTrackChunkOffset(dirChunk.value)
240+
if (chunkOffset <= location.offset && location.offset <= (chunkOffset + chunkLength.distance)) {
241+
val distanceToChunkStart = if (dirChunk.direction == Direction.INCREASING)
242+
location.offset - chunkOffset
243+
else
244+
(chunkOffset + chunkLength.distance) - location.offset
245+
return offsetAfterFirstChunk + distanceToChunkStart
246+
}
247+
}
248+
offsetAfterFirstChunk += chunkLength.distance
249+
}
250+
return null
251+
}
252+
253+
/** Returns the offset of a location on a given list of chunks, throws if not found */
254+
@JvmName("getOffsetOfTrackLocationOnChunksOrThrow")
255+
fun getOffsetOfTrackLocationOnChunksOrThrow(
256+
infra: TrackProperties,
257+
location: TrackLocation,
258+
chunks: DirStaticIdxList<TrackChunk>,
259+
): Distance {
260+
return getOffsetOfTrackLocationOnChunks(infra, location, chunks) ?: throw RuntimeException()
261+
}

0 commit comments

Comments
 (0)