From fd05d259105a9ad716d2d158eef11868246485cb Mon Sep 17 00:00:00 2001 From: Max Kannenberg <95709892+MaxKannenberg@users.noreply.github.com> Date: Fri, 18 Feb 2022 11:34:16 +0100 Subject: [PATCH] Change braking calculation from all in one step to multiple steps --- .../train_passenger_SiemensDesiroClassic.yaml | 2 +- src/Behavior.jl | 172 ++++++++++++------ src/EnergySaving.jl | 2 +- src/Import.jl | 2 +- src/Input.jl | 4 +- src/TrainRunCalc.jl | 18 +- 6 files changed, 127 insertions(+), 73 deletions(-) diff --git a/data/trains/train_passenger_SiemensDesiroClassic.yaml b/data/trains/train_passenger_SiemensDesiroClassic.yaml index 695dc8a..76500b8 100644 --- a/data/trains/train_passenger_SiemensDesiroClassic.yaml +++ b/data/trains/train_passenger_SiemensDesiroClassic.yaml @@ -19,7 +19,7 @@ train: # for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_t)/v00)^2) f_Rtd0: 3.0 # coefficient for basic resistance due to the traction units driving axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WL0" -> 2.5 ‰ to 3.5 ‰) f_Rtc0: 1.4 # coefficient for basic resistance due to the traction units carring axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WW0" -> 1.2 ‰ to 1.6 ‰) - F_Rt2: 2600 # coefficient for air resistance of the traction units (in N) (source: the closest parameters are used: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "Fzg. vierachsig, abgerundeter Kopf" plus "Sektion bei Mehrteiligkeit" -> 2200 N to 400 N) + F_Rt2: 2600 # coefficient for air resistance of the traction units (in N) (source: the closest parameters are used: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "Fzg. vierachsig, abgerundeter Kopf" plus "Sektion bei Mehrteiligkeit" -> 2200 N + 400 N) # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_w)/v00)^2)) f_Rw0: # coefficient for basic resistance of the set of wagons (in ‰) (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) diff --git a/src/Behavior.jl b/src/Behavior.jl index 1e4ada1..f859707 100644 --- a/src/Behavior.jl +++ b/src/Behavior.jl @@ -104,7 +104,7 @@ function moveAStep(previousPoint::Dict, stepVariable::String, stepSize::Real, cs # TODO: csId is only for error messages. Should it be removed? #= 08/31 TODO: How to check if the train stopps during this step? I should throw an error myself that I catch in higher hierarchies. =# - # creating the next data point + # create the next data point newPoint = createDataPoint() newPoint[:i] = previousPoint[:i]+1 # identifier @@ -220,7 +220,7 @@ function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real) return POI end end - error("ERROR in getNextPointOfInterest: There is no POI ist higher than s.") + error("ERROR in getNextPointOfInterest: There is no POI higher than s.") end #function getNextPointOfInterest ## This function calculates the data points of the breakFree section. @@ -393,7 +393,7 @@ function addAccelerationSection!(CS::Dict, drivingCourse::Vector{Dict}, settings else # if the level of approximation is reached if drivingCourse[end][:v] <= 0.0 # push!(BS[:dataPoints], drivingCourse[end][:i]) - error("ERROR: The train stops during the acceleration section in CS",CS[:id]," because the tractive effort is lower than the resistant forces.", + error("ERROR: The train stops during the acceleration section in CS",CS[:id]," between the positions ",drivingCourse[end-1][:s]," m and ",drivingCourse[end][:s]," m because the tractive effort is lower than the resistant forces.", " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", " F_T=",drivingCourse[end-1][:F_T]," N R_traction=",drivingCourse[end-1][:R_traction]," N R_wagons=",drivingCourse[end-1][:R_wagons]," N R_path=",drivingCourse[end-1][:R_path]," N.") @@ -433,9 +433,9 @@ function addAccelerationSection!(CS::Dict, drivingCourse::Vector{Dict}, settings CS[:t] = CS[:t] + BS[:t] # total running time (in s) CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - # TODO: this warning schould not be needed. just for testing if CS[:v_peak] < drivingCourse[end][:v] - println("WARNING, v is getting to high at the end of the acceleration section. v=",drivingCourse[end][:v] ," > v_peak=",CS[:v_peak]) + println("WARNING: v is getting to high at the end of the acceleration section. v=",drivingCourse[end][:v] ," > v_peak=",CS[:v_peak]) + # TODO: this warning should not be needed. just for testing end merge!(CS[:behaviorSections], Dict(:acceleration => BS)) @@ -899,7 +899,7 @@ function addCoastingSectionUntilBraking!(CS::Dict, drivingCourse::Vector{Dict}, # acceleration (in m/s^2): drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) - # creating the next data point + # create the next data point push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) @@ -1003,7 +1003,8 @@ end #function addCoastingSectionUntilBraking! ## This function calculates the data points of the braking section. (standard braking section with only two data points) # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. -function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) #, s_braking::AbstractFloat) +function addBrakingSectionInOneStep!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) + #function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) # function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, massModel::String, train::Dict, CSs::Vector{Dict}) #, s_braking::AbstractFloat) if drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:s] < CS[:s_exit] BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) @@ -1018,7 +1019,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dic # traction effort and resisting forces (in N) calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], BS[:s_exit]-drivingCourse[end][:s]) - # TODO: or just take train[:a_braking]? difference ist by 0.0000000001 m/s^2: drivingCourse[end][:a] = train[:a_braking] + # TODO: or just take train[:a_braking]? difference is by 0.0000000001 m/s^2: drivingCourse[end][:a] = train[:a_braking] # println("a_braking till ",nextPointOfInterest,": ", calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], BS[:s_exit]-drivingCourse[end][:s]), ". It should be: ",train[:a_braking]) # calculate the braking distance to the next point of interest @@ -1069,67 +1070,120 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dic merge!(CS[:behaviorSections], Dict(:braking=>BS)) end # else: return the characteristic section without a braking section return (CS, drivingCourse) -end #function addBrakingSection! +end #function addBrakingSectionInOneStep! ## This function calculates the data points of the braking section. # 09/07 new braking section with more than two data points # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. -function addBrakingSectionStepwise!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) #, s_braking::AbstractFloat) - #= TODO from 2022/01/22: integrate points of interest - if drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:s] < CS[:s_exit] - BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] +function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) + #function addBrakingSectionStepwise!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) + if settings[:stepVariable] != "s in m" + error("addBrakingSectionStepwise! does not work with other step variables than 's in m' right now.") + end + # TODO: add for loop to realize calculations with step variables t and v - currentStepSize=settings[:stepSize] # initialize the step size that can be reduced near intersections - velocityIsPositive = true - while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:s] < CS[:s_exit] && velocityIsPositive - # traction effort and resisting forces (in N): - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + # conditions for braking section + targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] + trainAtEnd = drivingCourse[end][:s] >= CS[:s_exit] - # acceleration (in m/s^2): - drivingCourse[end][:a] = train[:a_braking] + # use the conditions for the braking section + if !targetSpeedReached && !trainAtEnd + BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] - # creating the next data point + while !targetSpeedReached && !trainAtEnd + currentStepSize = settings[:stepSize] # initialize the step size that can be reduced near intersections + nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - #TODO moveAStep should give back true or false for success or failure e.g. with dropping below v=0 m/s - #at the moment it is only for stepVariable=="s in m" - if settings[:stepVariable] == "s in m" - if ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 - velocityIsPositive=false - break + while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest + # traction effort and resisting forces (in N): + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + + # acceleration (in m/s^2): + drivingCourse[end][:a] = train[:a_braking] + # TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s]) + + # create the next data point + #TODO moveAStep should give back true or false for success or failure e.g. with dropping below v=0 m/s + #at the moment it can only be used for stepVariable == "s in m" + if settings[:stepVariable] == "s in m" + if ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 + targetSpeedReached = true + + # create empty data point for setting the values after the while loop + push!(drivingCourse, createDataPoint()) + drivingCourse[end][:i] = drivingCourse[end-1][:i]+1 + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + break + end + else + error("Stepwise braking can currently be used with stepVariable=='s in m' only. Otherwise errors may not be detected.") end - end - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - # s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end # while + push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + end # while - if drivingCourse[end][:v] < CS[:v_exit] || !velocityIsPositive - # calculate s, t, v - drivingCourse[end][:s] = CS[:s_exit] # position (in m) - drivingCourse[end][:v] = CS[:v_exit] # velocity (in m/s) - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] # step size (in m) - drivingCourse[end][:Δv] = drivingCourse[end][:v] - drivingCourse[end-1][:v] # step size (in m/s) + if drivingCourse[end][:v] < CS[:v_exit] || targetSpeedReached + # reset last point with setting v=v_exit + targetSpeedReached = true + trainAtEnd = true - #drivingCourse[end-1][:a]=round(calcBrakingAcceleration(drivingCourse[end-1][:v], drivingCourse[end][:v], drivingCourse[end][:Δs]), digits=approximationLevel) # acceleration (in m/s^2) (rounding because it should not be less than a_braking) - drivingCourse[end-1][:a] = calcBrakingAcceleration(drivingCourse[end-1][:v], drivingCourse[end][:v], drivingCourse[end][:Δs]) - # if drivingCourse[end-1][:a]=0.0 - # println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",drivingCourse[end-1][:a] ," > ",train[:a_braking]) - # end - drivingCourse[end][:Δt] = calc_Δt_with_Δv(drivingCourse[end][:Δv], drivingCourse[end-1][:a]) # step size (in s) - drivingCourse[end][:t] = drivingCourse[end-1][:t] + drivingCourse[end][:Δt] # point in time (in s) + # calculate s, t, v + drivingCourse[end][:s] = CS[:s_exit] # position (in m) + drivingCourse[end][:v] = CS[:v_exit] # velocity (in m/s) + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] # step size (in m) + drivingCourse[end][:Δv] = drivingCourse[end][:v] - drivingCourse[end-1][:v] # step size (in m/s) - drivingCourse[end][:ΔW] = 0.0 # mechanical work in this step (in Ws) - drivingCourse[end][:W] = drivingCourse[end-1][:W] + drivingCourse[end][:ΔW] # mechanical work (in Ws) - drivingCourse[end][:ΔE] = drivingCourse[end][:ΔW] # energy consumption in this step (in Ws) - drivingCourse[end][:E] = drivingCourse[end-1][:E] + drivingCourse[end][:ΔE] # energy consumption (in Ws) - elseif drivingCourse[end][:s] > CS[:s_exit] - error("At the end of braking: s>s_exit but v>v_exit") - else + drivingCourse[end-1][:a] = calcBrakingAcceleration(drivingCourse[end-1][:v], drivingCourse[end][:v], drivingCourse[end][:Δs]) + if drivingCourse[end-1][:a]=0.0 + println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",drivingCourse[end-1][:a] ," > ",train[:a_braking]) + end + drivingCourse[end][:Δt] = calc_Δt_with_Δv(drivingCourse[end][:Δv], drivingCourse[end-1][:a]) # step size (in s) + drivingCourse[end][:t] = drivingCourse[end-1][:t] + drivingCourse[end][:Δt] # point in time (in s) - end + drivingCourse[end][:ΔW] = 0.0 # mechanical work in this step (in Ws) + drivingCourse[end][:W] = drivingCourse[end-1][:W] + drivingCourse[end][:ΔW] # mechanical work (in Ws) + drivingCourse[end][:ΔE] = drivingCourse[end][:ΔW] # energy consumption in this step (in Ws) + drivingCourse[end][:E] = drivingCourse[end-1][:E] + drivingCourse[end][:ΔE] # energy consumption (in Ws) + + + + elseif drivingCourse[end][:s] > CS[:s_exit] + trainAtEnd = true + error("At the end of braking: s>s_exit but v>v_exit") + elseif drivingCourse[end][:s] > nextPointOfInterest + # reset last point with lowering s to the nextPointOfInterest + + # calculate s, t, v + if settings[:stepVariable] == "s in m" + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] # step size (in m) + else + # TODO + end + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # create the next data point + push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + + elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] + targetSpeedReached = true + trainAtEnd = true + elseif drivingCourse[end][:v] == CS[:v_exit] + targetSpeedReached = true + error("At the end of braking: sv_exit") + else + # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest + end + end # calculate the accumulated coasting section information merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) @@ -1142,10 +1196,10 @@ function addBrakingSectionStepwise!(CS::Dict, drivingCourse::Vector{Dict}, setti CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) merge!(CS[:behaviorSections], Dict(:braking=>BS)) - end # else: return the characteristic section without a braking section - =# - return (CS, drivingCourse) -end #function addBrakingSectionStepwise! + end # else: return the characteristic section without a braking section + + return (CS, drivingCourse) +end #function addBrakingSection! ## This function calculates the data point of the standstill. diff --git a/src/EnergySaving.jl b/src/EnergySaving.jl index 7478cca..e0a52ee 100644 --- a/src/EnergySaving.jl +++ b/src/EnergySaving.jl @@ -1,4 +1,4 @@ -# INFO: EnergySaving should not be used because it is not completed yet. It was used to show the possiility of calculating different operation modes. +# INFO: EnergySaving should not be used because it is not completed yet. It was used to show the possibility of calculating different operation modes. # TODO: It has to be optimized so that each ernergy saving method is working individually for every train on every path. # TODO: calculation time for passenger trains on path1 is very long and should be reduced diff --git a/src/Import.jl b/src/Import.jl index 6702566..233aac5 100644 --- a/src/Import.jl +++ b/src/Import.jl @@ -15,7 +15,7 @@ function importYamlFiles(trainDirectory::String, pathDirectory::String, settings settings = importSettingsFromYaml(settingsDirectory) return (train, path, settings) - end #function importYamlFiles +end #function importYamlFiles """ Read the input information from one of the YAML files for train, path or settings, save it in a Dictionary and return it. diff --git a/src/Input.jl b/src/Input.jl index 59a301e..f133cbe 100644 --- a/src/Input.jl +++ b/src/Input.jl @@ -61,7 +61,7 @@ function checkAndSetTrain!(train::Dict) checkAndSetPositiveNumber!(train, "train", :f_Rw1, "‰", 0.0) # coefficient for the consists resistance to rolling (in ‰) checkAndSetPositiveNumber!(train, "train", :f_Rw2, "‰", 0.0) # coefficient fo the consistsr air resistance (in ‰) -# informAboutUnusedKeys(train, "train") # inform the user, which Symbols of the input dictionary are not used in this tool +# TODO: informAboutUnusedKeys(train, "train") # inform the user, which Symbols of the input dictionary are not used in this tool return train end #function checkAndSetTrain! @@ -394,7 +394,7 @@ function checkAndSetSections!(path::Dict) if length(checkedSections)>1 && sections[section][:s_start] != checkedSections[end-1][:s_end] - error("ERROR at checking the input dictionary for the path[:sections]: The starting position of the ",section,". section does not euqaul the last position of the previous section. The sections have to be sequential.") + error("ERROR at checking the input dictionary for the path[:sections]: The starting position of the ",section,". section (s=",sections[section][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? end end #for diff --git a/src/TrainRunCalc.jl b/src/TrainRunCalc.jl index d1ef152..25e3527 100644 --- a/src/TrainRunCalc.jl +++ b/src/TrainRunCalc.jl @@ -79,25 +79,26 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train s_breakFree = get(BSs, :breakFree, Dict(:length=>0.0))[:length] s_clearing = get(BSs, :clearing, Dict(:length=>0.0))[:length] s_acceleration = get(BSs, :acceleration, Dict(:length=>0.0))[:length] - s_braking = max(0.0, ceil((CS[:v_exit]^2-CS[:v_peak]^2)/2/train[:a_braking], digits=approximationLevel)) # ceil is used to be sure that the train reaches v_exit at s_exit in spite of rounding errors + s_braking = calcBrakingDistance(CS[:v_peak], CS[:v_exit], train[:a_braking]) + # old: s_braking = max(0.0, ceil((CS[:v_exit]^2-CS[:v_peak]^2)/2/train[:a_braking], digits=approximationLevel)) # ceil is used to be sure that the train reaches v_exit at s_exit in spite of rounding errors # calculate the cruising sections length - s_cruising = CS[:length] - s_breakFree - s_clearing - s_acceleration - s_braking + s_cruising = max(0.0, CS[:length] - s_breakFree - s_clearing - s_acceleration - s_braking) # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit CS[:behaviorSections] = Dict() CS[:E] = 0.0 CS[:t] = 0.0 +# TODO 02/09: could there be a better structure for processing the different moving phases? (this if fork was added on 2022/09/02) + if s_clearing > 0.0 && s_breakFree + s_acceleration == 0.0 + (CS, drivingCourse)=addCruisingSection!(CS, drivingCourse, s_clearing, settings, train, CSs, "clearing") + end - if s_clearing == CS[:length] - # 09/06 TODO: thought about using "cruising" because it is used in EnergySaving and not clearing (CS, drivingCourse)=addCruisingSection!(CS, drivingCourse, s_clearing, settings, train, CSs, "cruising") - (CS, drivingCourse)=addCruisingSection!(CS, drivingCourse, s_clearing, settings, train, CSs, "clearing") - elseif s_cruising == CS[:length] + if s_cruising == CS[:length] (CS, drivingCourse)=addCruisingSection!(CS, drivingCourse, s_cruising, settings, train, CSs, "cruising") elseif s_cruising > 0.0 || s_braking == 0.0 - # 09/21 elseif s_cruising > 0.0 - # 09/21 elseif s_cruising > 0.01 # if the cruising section is longer than 1 cm (because of rounding issues not >0.0) + if drivingCourse[end][:v] < CS[:v_peak] (CS, drivingCourse) = addAccelerationSection!(CS, drivingCourse, settings, train, CSs) end #if @@ -115,7 +116,6 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train (CS, drivingCourse)=addCruisingSection!(CS, drivingCourse, s_cruising, settings, train, CSs, "cruising") end else - if CS[:v_entry] < CS[:v_peak] || s_acceleration > 0.0 # or instead of " || s_acceleration > 0.0" use "v_entry <= v_peak" or "v_i <= v_peak" # 09/09 old (not sufficient for steep gradients): if CS[:v_entry] < CS[:v_peak] (CS, drivingCourse)=addAccelerationSectionUntilBraking!(CS, drivingCourse, settings, train, CSs)