Skip to content

Commit 488b282

Browse files
claraniBaptiste Prevot
authored andcommitted
core: update simulation to take into account extended dead sections
Co-authored-by: Baptiste Prevot <[email protected]>
1 parent b2a8a7b commit 488b282

File tree

13 files changed

+143
-61
lines changed

13 files changed

+143
-61
lines changed

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

+5
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ public EnvelopeSimContext(
2020
this.timeStep = timeStep;
2121
this.tractiveEffortCurveMap = tractiveEffortCurveMap;
2222
}
23+
24+
public EnvelopeSimContext updateCurves(
25+
RangeMap<Double, PhysicsRollingStock.TractiveEffortPoint[]> tractiveEffortCurveMap) {
26+
return new EnvelopeSimContext(rollingStock, path, timeStep, tractiveEffortCurveMap);
27+
}
2328
}

core/osrd-railjson/src/main/java/fr/sncf/osrd/railjson/schema/rollingstock/RJSRollingStock.java

+6
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ public class RJSRollingStock implements Identified {
8888
@Json(name = "loading_gauge")
8989
public RJSLoadingGaugeType loadingGauge = null;
9090

91+
@Json(name = "electrical_power_startup_time")
92+
public Double electricalPowerStartUpTime = null;
93+
94+
@Json(name = "raise_pantograph_time")
95+
public Double raisePantographTime = null;
96+
9197
public enum GammaType {
9298
CONST,
9399
MAX

core/src/main/java/fr/sncf/osrd/api/pathfinding/constraints/ElectrificationConstraints.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Collection<Pathfinding.Range> apply(SignalingRoute reservationRoute) {
2929
* because it needs electrified tracks and isn't compatible with the catenaries in some range
3030
*/
3131
private static Set<Pathfinding.Range> getBlockedRanges(RollingStock stock, ReservationRoute route) {
32-
if (!stock.isElectricOnly())
32+
if (stock.isThermal())
3333
return Set.of();
3434

3535
var res = new HashSet<Pathfinding.Range>();

core/src/main/java/fr/sncf/osrd/railjson/parser/RJSRollingStockParser.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ else if (!rjsRollingStock.railjsonVersion.equals(RJSRollingStock.CURRENT_VERSION
121121
modes,
122122
rjsRollingStock.effortCurves.defaultMode,
123123
rjsRollingStock.basePowerClass,
124-
rjsRollingStock.powerRestrictions
124+
rjsRollingStock.powerRestrictions,
125+
rjsRollingStock.electricalPowerStartUpTime,
126+
rjsRollingStock.raisePantographTime
125127
);
126128
}
127129

core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,14 @@ public static StandaloneSimResult run(
5959
for (var trainSchedule : schedules) {
6060
if (!cacheMaxEffort.containsKey(trainSchedule)) {
6161
var rollingStock = trainSchedule.rollingStock;
62+
6263
// MRSP & SpeedLimits
6364
var mrsp = MRSP.from(trainPath, rollingStock, true, trainSchedule.tag);
6465
var speedLimits = MRSP.from(trainPath, rollingStock, false, trainSchedule.tag);
6566
mrsp = driverBehaviour.applyToMRSP(mrsp);
6667
cacheSpeedLimits.put(trainSchedule, ResultEnvelopePoint.from(speedLimits));
6768

68-
// Base
69+
// Context
6970
var electrificationMap = envelopeSimPath.getElectrificationMap(rollingStock.basePowerClass,
7071
trainSchedule.powerRestrictionMap, rollingStock.powerRestrictions,
7172
trainSchedule.options.ignoreElectricalProfiles);
@@ -78,7 +79,18 @@ public static StandaloneSimResult run(
7879
curvesAndConditions.conditions(), electrificationMap));
7980
cachePowerRestrictionRanges.put(trainSchedule, PowerRestrictionRange.from(
8081
curvesAndConditions.conditions(), trainSchedule.powerRestrictionMap));
81-
var envelope = computeMaxEffortEnvelope(context, mrsp, trainSchedule);
82+
83+
// MaxSpeedEnvelope
84+
var maxSpeedEnvelope = MaxSpeedEnvelope.from(context, trainSchedule.getStopsPositions(), mrsp);
85+
86+
// MaxEffortEnvelope
87+
// need to compute a new effort curve mapping with the maxSpeedEnvelope in order to extend the
88+
// neutral sections (with time to lower/raise pantograph...)
89+
context = context.updateCurves(
90+
rollingStock.addNeutralSystemTimes(electrificationMap, trainSchedule.comfort, maxSpeedEnvelope,
91+
context.tractiveEffortCurveMap));
92+
var envelope = MaxEffortEnvelope.from(context, trainSchedule.initialSpeed, maxSpeedEnvelope);
93+
8294
var simResultTrain = ScheduleMetadataExtractor.run(
8395
envelope,
8496
trainPath,
@@ -140,19 +152,6 @@ public static StandaloneSimResult runFromRJS(
140152
);
141153
}
142154

143-
/**
144-
* Compute the max effort envelope given a path, MRSP and a schedule
145-
*/
146-
public static Envelope computeMaxEffortEnvelope(
147-
EnvelopeSimContext context,
148-
Envelope mrsp,
149-
StandaloneTrainSchedule schedule
150-
) {
151-
final var stops = schedule.getStopsPositions();
152-
final var maxSpeedEnvelope = MaxSpeedEnvelope.from(context, stops, mrsp);
153-
return MaxEffortEnvelope.from(context, schedule.initialSpeed, maxSpeedEnvelope);
154-
}
155-
156155
/**
157156
* Apply a list of allowances, in order
158157
*/

core/src/main/java/fr/sncf/osrd/train/RollingStock.java

+97-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package fr.sncf.osrd.train;
22

33
import com.google.common.collect.ImmutableRangeMap;
4+
import com.google.common.collect.Range;
45
import com.google.common.collect.RangeMap;
56
import com.google.common.collect.TreeRangeMap;
67
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
8+
import fr.sncf.osrd.envelope.Envelope;
79
import fr.sncf.osrd.envelope_sim.PhysicsRollingStock;
810
import fr.sncf.osrd.envelope_sim.electrification.Electrification;
911
import fr.sncf.osrd.envelope_sim.electrification.Electrified;
@@ -96,6 +98,8 @@ public class RollingStock implements PhysicsRollingStock {
9698
private final String defaultMode;
9799
public final String basePowerClass;
98100
public final Map<String, String> powerRestrictions;
101+
public final Double electricalPowerStartUpTime;
102+
public final Double raisePantographTime;
99103

100104
@Override
101105
public double getMass() {
@@ -188,17 +192,18 @@ public double getDeceleration() {
188192
}
189193

190194
/**
191-
* Returns the tractive effort curve that matches best, along with the infra conditions that matched
195+
* Returns the tractive effort curve that matches best, along with the electrification conditions that matched
192196
*/
193197
protected CurveAndCondition findTractiveEffortCurve(Comfort comfort, Electrification electrification) {
194198
if (electrification instanceof Neutral n) {
195-
var wouldUseRes = findTractiveEffortCurve(comfort, n.overlappedElectrification);
196-
if (!modes.get(wouldUseRes.cond.mode).isElectric) {
197-
return wouldUseRes;
198-
} else {
199-
return new CurveAndCondition(COASTING_CURVE, new InfraConditions(null, null, null));
199+
var overlappedCurve = findTractiveEffortCurve(comfort, n.overlappedElectrification);
200+
var isOverlappedCurveThermal = !modes.get(overlappedCurve.cond.mode).isElectric;
201+
if (isOverlappedCurveThermal) {
202+
return overlappedCurve;
200203
}
201-
} else if (electrification instanceof NonElectrified) {
204+
return new CurveAndCondition(COASTING_CURVE, new InfraConditions(null, null, null));
205+
}
206+
if (electrification instanceof NonElectrified) {
202207
return new CurveAndCondition(modes.get(defaultMode).defaultCurve,
203208
new InfraConditions(defaultMode, null, null));
204209
}
@@ -221,6 +226,7 @@ protected CurveAndCondition findTractiveEffortCurve(Comfort comfort, Electrifica
221226

222227
/**
223228
* Returns the tractive effort curves corresponding to the electrical conditions map
229+
* The neutral sections are not extended in this function.
224230
*
225231
* @param electrificationMap The map of electrification conditions to use
226232
* @param comfort The comfort level to get the curves for
@@ -238,19 +244,88 @@ public CurvesAndConditions mapTractiveEffortCurves(RangeMap<Double, Electrificat
238244
return new CurvesAndConditions(ImmutableRangeMap.copyOf(res), ImmutableRangeMap.copyOf(conditionsUsed));
239245
}
240246

247+
248+
protected Range<Double> computeDeadSectionRange(Range<Double> neutralRange, Neutral n, Envelope maxEffortEnvelope) {
249+
var endRange = neutralRange.upperEndpoint();
250+
var finalSpeed = maxEffortEnvelope.interpolateSpeedLeftDir(endRange, 1);
251+
double additionalRange = finalSpeed * electricalPowerStartUpTime;
252+
if (n.lowerPantograph) {
253+
additionalRange += finalSpeed * raisePantographTime;
254+
}
255+
return Range.closed(neutralRange.lowerEndpoint(), neutralRange.upperEndpoint() + additionalRange);
256+
}
257+
258+
/**
259+
* Returns the tractive effort curves corresponding to the electrical conditions map with neutral sections
260+
*
261+
* @param electrificationMap The map of electrification conditions to use
262+
* @param comfort The comfort level to get the curves for
263+
* @param maxSpeedEnvelope The maxSpeedEnvelope used to extend the neutral sections
264+
*/
265+
public RangeMap<Double, TractiveEffortPoint[]> addNeutralSystemTimes(
266+
RangeMap<Double, Electrification> electrificationMap, Comfort comfort, Envelope maxSpeedEnvelope,
267+
RangeMap<Double, TractiveEffortPoint[]> curvesUsed) {
268+
269+
TreeRangeMap<Double, TractiveEffortPoint[]> newCurves = TreeRangeMap.create();
270+
newCurves.putAll(curvesUsed);
271+
272+
for (var elecCondEntry : electrificationMap.asMapOfRanges().entrySet()) {
273+
if (elecCondEntry.getValue() instanceof Neutral n) {
274+
// estimate the distance during which the train will be coasting, due to having respected the
275+
// neutral section
276+
var deadSectionRange = computeDeadSectionRange(elecCondEntry.getKey(), n, maxSpeedEnvelope);
277+
var curveAndCondition = findTractiveEffortCurve(comfort, n);
278+
if (curveAndCondition.cond.mode == null) { // The train is effectively coasting
279+
newCurves.put(deadSectionRange, curveAndCondition.curve);
280+
}
281+
}
282+
}
283+
return ImmutableRangeMap.copyOf(newCurves);
284+
}
285+
241286
public Set<String> getModeNames() {
242287
return modes.keySet();
243288
}
244289

245290
/**
246-
* Return whether this rolling stock support only electric modes
291+
* Return whether this rolling stock's default mode is thermal
247292
*/
248-
public boolean isElectricOnly() {
249-
for (var mode : modes.values()) {
250-
if (!mode.isElectric)
251-
return false;
252-
}
253-
return true;
293+
public boolean isThermal() {
294+
return !modes.get(defaultMode).isElectric();
295+
}
296+
297+
/**
298+
* Return whether this rolling stock's has an electric mode
299+
*/
300+
public final boolean isElectric() {
301+
return modes.values().stream().anyMatch(ModeEffortCurves::isElectric);
302+
}
303+
304+
/**
305+
* Creates a new rolling stock (a physical train inventory item).
306+
*/
307+
public RollingStock(
308+
String id,
309+
double length,
310+
double mass,
311+
double inertiaCoefficient,
312+
double a,
313+
double b,
314+
double c,
315+
double maxSpeed,
316+
double startUpTime,
317+
double startUpAcceleration,
318+
double comfortAcceleration,
319+
double gamma,
320+
GammaType gammaType,
321+
RJSLoadingGaugeType loadingGaugeType,
322+
Map<String, ModeEffortCurves> modes,
323+
String defaultMode,
324+
String basePowerclass
325+
) {
326+
this(id, length, mass, inertiaCoefficient, a, b, c, maxSpeed, startUpTime, startUpAcceleration,
327+
comfortAcceleration, gamma, gammaType, loadingGaugeType, modes, defaultMode, basePowerclass, Map.of(),
328+
0., 0.);
254329
}
255330

256331
/**
@@ -274,7 +349,9 @@ public RollingStock(
274349
Map<String, ModeEffortCurves> modes,
275350
String defaultMode,
276351
String basePowerclass,
277-
Map<String, String> powerRestrictions
352+
Map<String, String> powerRestrictions,
353+
Double electricalPowerStartUpTime,
354+
Double raisePantographTime
278355
) {
279356
this.id = id;
280357
this.A = a;
@@ -295,5 +372,10 @@ public RollingStock(
295372
this.loadingGaugeType = loadingGaugeType;
296373
this.basePowerClass = basePowerclass;
297374
this.powerRestrictions = powerRestrictions;
375+
this.electricalPowerStartUpTime = electricalPowerStartUpTime;
376+
this.raisePantographTime = raisePantographTime;
377+
378+
assert !isElectric() || (this.electricalPowerStartUpTime != null && this.raisePantographTime != null) :
379+
"Electrical power start up time and Raise pantograph time must be defined for an electric train";
298380
}
299381
}

core/src/test/java/fr/sncf/osrd/train/TestTrains.java

+9-13
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
107107
RJSLoadingGaugeType.G1,
108108
linearModeEffortCurves,
109109
"thermal",
110-
"1",
111-
Map.of()
110+
"1"
112111
);
113112

114113
VERY_LONG_FAST_TRAIN = new RollingStock(
@@ -125,8 +124,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
125124
RJSLoadingGaugeType.G1,
126125
linearModeEffortCurves,
127126
"thermal",
128-
"1",
129-
Map.of()
127+
"1"
130128
);
131129

132130
REALISTIC_FAST_TRAIN = new RollingStock(
@@ -144,7 +142,9 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
144142
complexModeEffortCurves,
145143
"thermal",
146144
"5",
147-
Map.of("Restrict1", "4", "Restrict2", "3")
145+
Map.of("Restrict1", "4", "Restrict2", "3"),
146+
0.,
147+
0.
148148
);
149149

150150
REALISTIC_FAST_TRAIN_MAX_DEC_TYPE = new RollingStock(
@@ -161,8 +161,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
161161
RJSLoadingGaugeType.G1,
162162
linearModeEffortCurves,
163163
"thermal",
164-
"1",
165-
Map.of()
164+
"1"
166165
);
167166

168167
FAST_TRAIN_LARGE_GAUGE = new RollingStock(
@@ -179,8 +178,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
179178
RJSLoadingGaugeType.GC,
180179
linearModeEffortCurves,
181180
"thermal",
182-
"1",
183-
Map.of()
181+
"1"
184182
);
185183

186184
FAST_ELECTRIC_TRAIN = new RollingStock(
@@ -198,8 +196,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
198196
createModeEffortCurves(MAX_SPEED, CurveShape.LINEAR,
199197
Map.of("25000", new RollingStock.EffortCurveConditions[0])),
200198
"25000",
201-
"1",
202-
Map.of()
199+
"1"
203200
);
204201

205202
CONSTANT_POWER_TRAIN = new RollingStock(
@@ -217,8 +214,7 @@ private static Map<String, RollingStock.ModeEffortCurves> createModeEffortCurves
217214
createModeEffortCurves(MAX_SPEED, CurveShape.HYPERBOLIC,
218215
Map.of("thermal", new RollingStock.EffortCurveConditions[0])),
219216
"thermal",
220-
"1",
221-
Map.of()
217+
"1"
222218
);
223219
}
224220

python/railjson_generator/railjson_generator/examples/rolling_stocks/electric_rolling_stock.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -1903,5 +1903,7 @@
19031903
"C1": 3,
19041904
"C2": 1
19051905
},
1906-
"energy_sources": []
1906+
"energy_sources": [],
1907+
"electrical_power_startup_time": 5.0,
1908+
"raise_pantograph_time": 15.0
19071909
}

python/railjson_generator/railjson_generator/examples/rolling_stocks/fast_rolling_stock.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,5 @@
8989
"unit": ""
9090
},
9191
"power_restrictions": {},
92-
"energy_sources": [],
93-
"electrical_power_startup_time": 5.0,
94-
"raise_pantograph_time": 15.0
92+
"energy_sources": []
9593
}

python/railjson_generator/railjson_generator/examples/rolling_stocks/fast_rolling_stock_high_gamma.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,5 @@
7777
}
7878
},
7979
"base_power_class": "1",
80-
"energy_sources": [],
81-
"electrical_power_startup_time": null,
82-
"raise_pantograph_time": null
80+
"energy_sources": []
8381
}

python/railjson_generator/railjson_generator/examples/rolling_stocks/short_fast_rolling_stock.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,5 @@
7777
}
7878
},
7979
"base_power_class": "1",
80-
"energy_sources": [],
81-
"electrical_power_startup_time": null,
82-
"raise_pantograph_time": null
80+
"energy_sources": []
8381
}

python/railjson_generator/railjson_generator/examples/rolling_stocks/short_slow_rolling_stock.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,5 @@
7777
}
7878
},
7979
"base_power_class": "1",
80-
"energy_sources": [],
81-
"electrical_power_startup_time": null,
82-
"raise_pantograph_time": null
80+
"energy_sources": []
8381
}

python/railjson_generator/railjson_generator/examples/rolling_stocks/slow_rolling_stock.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,5 @@
7777
}
7878
},
7979
"base_power_class": "1",
80-
"energy_sources": [],
81-
"electrical_power_startup_time": null,
82-
"raise_pantograph_time": null
80+
"energy_sources": []
8381
}

0 commit comments

Comments
 (0)