Refactor TrainRunCalc.jl for better readability

development
Max Kannenberg 2022-02-23 14:03:21 +01:00
parent 29c4ec0d5b
commit fde1027b92
3 changed files with 112 additions and 271 deletions

View File

@ -326,163 +326,8 @@ end #function addBreakFreeSection!
## This function calculates the data points of the acceleration section.
# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the acceleration section
function addAccelerationSectionWithoutBraking!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict})
#=if drivingCourse would also be part of movingSectiong: function addAccelerationSection!(movingSection::Dict, csId::Integer, settings::Dict, train::Dict)
CSs = movingSection[:characteristicSections]
CS = CSs[csId]
drivingCourse = movingSection[:drivingCourse]=#
if drivingCourse[end][:v] == 0.0
(CS, drivingCourse) = addBreakFreeSection!(CS, drivingCourse, settings, train, CSs)
end #if
calculateForces!(drivingCourse[end], CSs, CS[:id], "acceleration", train, settings[:massModel])
if drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
(CS, drivingCourse) = addDiminishingSection!(CS, drivingCourse, settings, train, CSs)
calculateForces!(drivingCourse[end], CSs, CS[:id], "acceleration", train, settings[:massModel])
end
# if the tail of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept
formerSpeedLimits = detectFormerSpeedLimits(CSs, CS[:id], drivingCourse[end], train[:length])
# conditions for acceleration section
targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak]
trainAtEnd = drivingCourse[end][:s] >= CS[:s_exit]
tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R]
# use the conditions for the acceleration section
if !targetSpeedReached && !trainAtEnd && tractionSurplus
#11/23 long version: if drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:s] <CS[:s_exit] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R]
BS = createBehaviorSection("acceleration", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i])
drivingCourse[end][:behavior] = BS[:type]
while !targetSpeedReached && !trainAtEnd && tractionSurplus
currentStepSize = settings[:stepSize] # initialize the step size that can be reduced near intersections
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R]
# acceleration (in m/s^2):
drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train])
# 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])
if length(formerSpeedLimits) > 0 # If the tail of the train is located in a former characteristic section with lower speed limit check if is is possible to accelerate as normal
(CS, drivingCourse, formerSpeedLimits, BS, endOfCsReached) = considerFormerSpeedLimits!(CS, drivingCourse, settings, train, CSs, formerSpeedLimits, BS)
if endOfCsReached
return (CS, drivingCourse)
end #if
currentStepSize = settings[:stepSize] # initialize the step size that can be reduced near intersections
end #if
# traction effort and resisting forces (in N)
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel])
end #while
# check which limit was reached and adjust the currentStepSize for the next cycle
if cycle < approximationLevel+1
if drivingCourse[end][:v] <= 0.0
currentStepSize = settings[:stepSize] / 10.0^cycle
elseif drivingCourse[end][:F_T] <= drivingCourse[end][:F_R]
currentStepSize = settings[:stepSize] / 10.0^cycle
elseif drivingCourse[end][:s] > nextPointOfInterest
if settings[:stepVariable] == "s in m"
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
else
currentStepSize = settings[:stepSize] / 10.0^cycle
end
elseif drivingCourse[end][:v] > CS[:v_peak]
if settings[:stepVariable] == "v in m/s"
currentStepSize = CS[:v_peak]-drivingCourse[end-1][:v]
else
currentStepSize = settings[:stepSize] / 10.0^cycle
end
elseif drivingCourse[end][:s] == CS[:s_exit]
trainAtEnd = true
break
elseif drivingCourse[end][:v] == CS[:v_peak]
targetSpeedReached = true
break
elseif drivingCourse[end][:s] == nextPointOfInterest
break
else
error("ERROR at acceleration section: With the step variable ", settings[:stepVariable]," the while loop will be left although v<v_peak and s<s_exit in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s")
end
# delete last data point for recalculating the last step with reduced step size
pop!(drivingCourse)
pop!(BS[:dataPoints])
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]," 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.")
elseif drivingCourse[end][:v] > CS[:v_peak]
targetSpeedReached = true
pop!(drivingCourse)
pop!(BS[:dataPoints])
elseif drivingCourse[end][:s] > nextPointOfInterest
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
elseif drivingCourse[end][:F_T] <= drivingCourse[end][:F_R]
tractionSurplus = false
(CS, drivingCourse) = addDiminishingSection!(CS, drivingCourse, settings, train, CSs)
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel])
else
end #if
# TODO is it possible to put this into to the if-fork?
if drivingCourse[end][:s] == CS[:s_exit]
trainAtEnd = true
end
end #if
end #for
end #while
if length(BS[:dataPoints]) > 1 # it is possible that the acceleration starts at v_peak, accelerates a step, is to high and drops the last point. then there is only one data point which is not a section.
# calculate the accumulated acceleration section information
merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m)
:s_exit => drivingCourse[end][:s], # last position (in m)
:t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s)
:E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws)
:v_exit => drivingCourse[end][:v])) # exit speed (in m/s)))
CS[:t] = CS[:t] + BS[:t] # total running time (in s)
CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws)
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])
# TODO: this warning should not be needed. just for testing
end
merge!(CS[:behaviorSections], Dict(:acceleration => BS))
end
end
return (CS, drivingCourse)
end #function addAccelerationSectionWithoutBraking!
## This function calculates the data points of the acceleration section.
function addAccelerationSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool)
#function addAccelerationSectionUntilBraking!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict})
#=if drivingCourse would also be part of movingSectiong: function addAccelerationSection!(movingSection::Dict, csId::Integer, settings::Dict, train::Dict)
#=if drivingCourse would also be part of movingSectiong: function addAccelerationSection!(movingSection::Dict, csId::Integer, settings::Dict, train::Dict, ignoreBraking::Bool)
CSs = movingSection[:characteristicSections]
CS = CSs[csId]
drivingCourse = movingSection[:drivingCourse]=#
@ -1081,78 +926,6 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Di
end #function addCoastingSection!
## 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 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])
BS[:s_exit] = CS[:s_exit] # last position (in m)
drivingCourse[end][:behavior] = BS[:type]
while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:s] < BS[:s_exit]
nextPointOfInterest = min(BS[:s_exit], getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]))
if nextPointOfInterest < BS[:s_exit]
# 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 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
stepSize = nextPointOfInterest - drivingCourse[end][:s]
# create the next data point
push!(drivingCourse, moveAStep(drivingCourse[end], "s in m", stepSize, CS[:id]))
drivingCourse[end][:behavior] = BS[:type]
push!(BS[:dataPoints], drivingCourse[end][:i])
else # so if nextPointOfInterest == BS[:s_exit]
# traction effort and resisting forces (in N)
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel])
push!(drivingCourse, createDataPoint())
drivingCourse[end][:i] = drivingCourse[end-1][:i]+1 # incrementing the number of the data point
drivingCourse[end][:behavior] = BS[:type]
push!(BS[:dataPoints], drivingCourse[end][:i]) # refering from the breaking section to the last of its data points
# calculate s, t, v
drivingCourse[end][:s] = BS[: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-1][:a] = calcBrakingAcceleration(drivingCourse[end-1][:v], drivingCourse[end][:v], drivingCourse[end][:Δs])
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)
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)
break
end #if
end #while
merge!(BS, Dict(:length => drivingCourse[end][:Δs], # total length (in m)
#:s_exit => drivingCourse[end][:s], # last position (in m)
:t => drivingCourse[end][:Δt], # total running time (in s)
:E => drivingCourse[end][:ΔE], # total energy consumption (in Ws)
:v_exit => drivingCourse[end][:v])) # exit speed (in m/s)))
CS[:t] = CS[:t] + BS[:t] # total running time (in s)
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 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 addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict})

View File

@ -14,10 +14,11 @@ export preparateSections
## create a moving section and its containing characteristic sections with secured braking, acceleration and cruising behavior
function preparateSections(path::Dict, train::Dict, settings::Dict)
movingSection=createMovingSection(path, train[:v_limit])
movingSection=secureBrakingBehavior!(movingSection, train[:a_braking])
movingSection=secureAccelerationBehavior!(movingSection, settings, train)
movingSection=secureCruisingBehavior!(movingSection, settings, train)
movingSection = createMovingSection(path, train[:v_limit])
movingSection = secureBrakingBehavior!(movingSection, train[:a_braking])
movingSection = secureAccelerationBehavior!(movingSection, settings, train)
movingSection = secureCruisingBehavior!(movingSection, settings, train)
return movingSection
end #function preparateSections
@ -95,17 +96,25 @@ function secureBrakingBehavior!(movingSection::Dict, a_braking::Real)
CSs = movingSection[:characteristicSections]
csId = length(CSs)
CSs[csId][:v_exit] = 0.0 # the exit velocity of the last characteristic section is 0.0 m/s
followingCSv_entry = 0.0 # the exit velocity of the last characteristic section is 0.0 m/s
while csId >= 1
v_entryMax = calcBrakingStartVelocity(CSs[csId][:v_exit], a_braking, CSs[csId][:length])
#v_entryMax=floor(v_entryMax, digits=12)
CS = CSs[csId]
CSs[csId][:v_entry] = min(CSs[csId][:v_limit], v_entryMax)
CSs[csId][:v_peak] = CSs[csId][:v_entry]
CS[:v_exit] = min(CS[:v_limit], followingCSv_entry)
v_entryMax = calcBrakingStartVelocity(CS[:v_exit], a_braking, CS[:length])
CS[:v_entry] = min(CS[:v_limit], v_entryMax)
CS[:v_peak] = CS[:v_entry]
# 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
followingCSv_entry = CS[:v_entry]
csId = csId - 1
if csId >= 1
CSs[csId][:v_exit]=min(CSs[csId][:v_limit], CSs[csId+1][:v_entry])
end #if
end #while
return movingSection
end #function secureBrakingBehavior!
@ -120,23 +129,27 @@ function secureAccelerationBehavior!(movingSection::Dict, settings::Dict, train:
startingPoint[:i] = 1
previousCSv_exit = CSs[1][:v_entry]
for csId in 1:length(CSs)
CSs[csId][:v_entry] = min(CSs[csId][:v_entry], previousCSv_exit)
for CS in CSs
CS[:v_entry] = min(CS[:v_entry], previousCSv_exit)
startingPoint[:s] = CSs[csId][:s_entry]
startingPoint[:v] = CSs[csId][:v_entry]
startingPoint[:s] = CS[:s_entry]
startingPoint[:v] = CS[:v_entry]
accelerationCourse::Vector{Dict} = [startingPoint] # List of data points
if CSs[csId][:v_entry] < CSs[csId][:v_peak]
# 02/22 old (CSs[csId], accelerationCourse) = addAccelerationSection!(CSs[csId], accelerationCourse, settings, train, CSs) # this function changes the accelerationCourse
(CSs[csId], accelerationCourse) = addAccelerationSection!(CSs[csId], accelerationCourse, settings, train, CSs, true) # this function changes the accelerationCourse
CSs[csId][:v_peak] = max(CSs[csId][:v_entry], accelerationCourse[end][:v])
CSs[csId][:v_exit] = min(CSs[csId][:v_exit], CSs[csId][:v_peak], accelerationCourse[end][:v])
else #CSs[csId][:v_entry]==CSs[csId][:v_peak]
if CS[:v_entry] < CS[:v_peak]
(CS, accelerationCourse) = addAccelerationSection!(CS, accelerationCourse, settings, train, CSs, true) # this function changes the accelerationCourse
CS[:v_peak] = max(CS[:v_entry], accelerationCourse[end][:v])
CS[:v_exit] = min(CS[:v_exit], CS[:v_peak], accelerationCourse[end][:v])
else #CS[:v_entry] == CS[:v_peak]
# v_exit stays the same
end #if
previousCSv_exit=CSs[csId][:v_exit]
previousCSv_exit = CS[:v_exit]
# 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
end #for
return movingSection
@ -154,17 +167,22 @@ function secureCruisingBehavior!(movingSection::Dict, settings::Dict, train::Dic
previousCSv_exit = CSs[1][:v_entry]
for csId in 1:length(CSs)
CSs[csId][:v_entry] = min(CSs[csId][:v_entry], previousCSv_exit)
for CS in CSs
CS[:v_entry] = min(CS[:v_entry], previousCSv_exit)
startingPoint[:s] = CSs[csId][:s_entry]
startingPoint[:v] = CSs[csId][:v_peak]
startingPoint[:s] = CS[:s_entry]
startingPoint[:v] = CS[:v_peak]
cruisingCourse::Vector{Dict} = [startingPoint] # List of data points
(CSs[csId], cruisingCourse) = addCruisingSection!(CSs[csId], cruisingCourse, CSs[csId][:length], settings, train, CSs, "cruising") # this function changes the cruisingCourse
CSs[csId][:v_exit] = min(CSs[csId][:v_exit], cruisingCourse[end][:v])
(CS, cruisingCourse) = addCruisingSection!(CS, cruisingCourse, CS[:length], settings, train, CSs, "cruising") # this function changes the cruisingCourse
CS[:v_exit] = min(CS[:v_exit], cruisingCourse[end][:v])
previousCSv_exit = CSs[csId][:v_exit]
previousCSv_exit = CS[:v_exit]
# 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
end #for
return movingSection

View File

@ -77,21 +77,74 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train
startingPoint[:s]=CSs[1][:s_entry]
drivingCourse::Vector{Dict} = [startingPoint] # List of data points
# for CS in CSs
for csId in 1:length(CSs)
CS = CSs[csId]
# for testing
if drivingCourse[end][:s] != CS[:s_entry]
println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry])
end
if drivingCourse[end][:v] > CS[:v_entry]
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry])
end
if drivingCourse[end][:v] < CS[:v_peak]
(CS, drivingCourse) = addAccelerationSection!(CS, drivingCourse, settings, train, CSs, false)
end #if
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0 # TODO: define a minimum cruising length?
(CS, drivingCourse) = addCruisingSection!(CS, drivingCourse, s_cruising, settings, train, CSs, "cruising")
end
if drivingCourse[end][:v] > CS[:v_exit]
(CS, drivingCourse)=addBrakingSection!(CS, drivingCourse, settings, train, CSs)
end #if
# for testing:
if drivingCourse[end][:s] != CS[:s_exit]
println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit])
end
if drivingCourse[end][:v] > CS[:v_exit]
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit])
end
end #for
(CSs[end], drivingCourse) = addStandstill!(CSs[end], drivingCourse, settings, train, CSs)
movingSection[:t] = drivingCourse[end][:t] # total running time (in s)
movingSection[:E] = drivingCourse[end][:E] # total energy consumption (in Ws)
return (movingSection, drivingCourse)
end #function calculateMinimumRunningTime
end # module TrainRunCalc
#=
# calculate a train run focussing on using the minimum possible running time
function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train::Dict)
CSs::Vector{Dict} = movingSection[:characteristicSections]
startingPoint=createDataPoint()
startingPoint[:i]=1
startingPoint[:s]=CSs[1][:s_entry]
drivingCourse::Vector{Dict} = [startingPoint] # List of data points
for csId in 1:length(CSs)
CS = CSs[csId]
BSs = CS[:behaviorSections]
# for testing:
if drivingCourse[end][:s] != CS[:s_entry]
if haskey(BSs, :cruising)
println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry])
end
println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry])
end
if drivingCourse[end][:v] > CS[:v_entry]
if haskey(BSs, :cruising)
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry])
end
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry])
end
# check if the CS has a cruising section
@ -151,14 +204,10 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train
# for testing:
if drivingCourse[end][:s] != CS[:s_exit]
if haskey(BSs, :cruising)
println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit])
end
println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit])
end
if drivingCourse[end][:v] > CS[:v_exit]
if haskey(BSs, :cruising)
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit])
end
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit])
end
end #for
@ -172,3 +221,4 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train
end #function calculateMinimumRunningTime
end # module TrainRunCalc
=#