161 lines
8.5 KiB
Julia
161 lines
8.5 KiB
Julia
|
#!/usr/bin/env julia
|
||
|
# -*- coding: UTF-8 -*-
|
||
|
# __julia-version__ = 1.7.2
|
||
|
# __author__ = "Max Kannenberg"
|
||
|
# __copyright__ = "2020-2022"
|
||
|
# __license__ = "ISC"
|
||
|
|
||
|
# Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`.
|
||
|
|
||
|
"""
|
||
|
trainRun(train::Dict, path::Dict, settings::Settings)
|
||
|
|
||
|
Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding dictionaries `train`, `path`, `settings`.
|
||
|
|
||
|
# Examples
|
||
|
```julia-repl
|
||
|
julia> trainRun(trainDict, pathDict)
|
||
|
todo !!!
|
||
|
```
|
||
|
"""
|
||
|
function trainRun(trainInput::Dict, pathInput::Dict, settings=Settings()::Settings)
|
||
|
# copy Input data for not changing them
|
||
|
# TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest)
|
||
|
train = copy(trainInput)
|
||
|
path = copy(pathInput)
|
||
|
|
||
|
# check the input data
|
||
|
(train, path) = checkAndSetInput!(train, path, settings)
|
||
|
settings.outputDetail == :everything && println("The input has been checked.")
|
||
|
|
||
|
# prepare the input data
|
||
|
movingSection = determineCharacteristics(path, train, settings)
|
||
|
settings.outputDetail == :everything && println("The moving section has been prepared.")
|
||
|
|
||
|
# calculate the train run for oparation mode "minimum running time"
|
||
|
(movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train)
|
||
|
settings.outputDetail == :everything && println("The driving course for the shortest running time has been calculated.")
|
||
|
|
||
|
# accumulate data and create an output dictionary
|
||
|
output = createOutput(train, settings, path, movingSection, drivingCourse)
|
||
|
|
||
|
return output
|
||
|
end # function trainRun
|
||
|
|
||
|
# calculate a train run focussing on using the minimum possible running time
|
||
|
function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict)
|
||
|
CSs::Vector{Dict} = movingSection[:characteristicSections]
|
||
|
|
||
|
if settings.massModel == :homogeneous_strip && settings.stepVariable == speed
|
||
|
println("WARNING: ! ! ! TrainRun.jl doesn't work reliably for the mass model homogeneous strip with step size v in m/s. The calculation time can be extremely high when calcutlating paths with steep gradients ! ! !")
|
||
|
end
|
||
|
|
||
|
startingPoint=createDataPoint()
|
||
|
startingPoint[:i]=1
|
||
|
startingPoint[:s]=CSs[1][:s_entry]
|
||
|
calculateForces!(startingPoint, CSs, 1, "default", train, settings.massModel) # traction effort and resisting forces (in N)
|
||
|
drivingCourse::Vector{Dict} = [startingPoint] # List of data points
|
||
|
|
||
|
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
|
||
|
|
||
|
# determine the different flags for switching between the states for creatinge moving phases
|
||
|
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
|
||
|
calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N)
|
||
|
|
||
|
previousSpeedLimitReached = false
|
||
|
stateFlags = Dict(:endOfCSReached => drivingCourse[end][:s] > CS[:s_exit],
|
||
|
:brakingStartReached => drivingCourse[end][:s] + s_braking == CS[:s_exit],
|
||
|
:tractionDeficit => drivingCourse[end][:F_T] < drivingCourse[end][:F_R], # or add another flag for equal forces?
|
||
|
:resistingForceNegative => drivingCourse[end][:F_R] < 0.0,
|
||
|
:previousSpeedLimitReached => false, #speedLimitReached, # check already at this position?
|
||
|
:speedLimitReached => drivingCourse[end][:v] > CS[:v_limit],
|
||
|
:error => false)
|
||
|
|
||
|
# determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill")
|
||
|
while !stateFlags[:endOfCSReached] # s < s_exit
|
||
|
if !stateFlags[:brakingStartReached] # s+s_braking < s_exit
|
||
|
if !stateFlags[:tractionDeficit]
|
||
|
if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0
|
||
|
(CS, drivingCourse, stateFlags) = addBreakFreeSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
|
||
|
|
||
|
elseif stateFlags[:previousSpeedLimitReached]
|
||
|
(CS, drivingCourse, stateFlags) = addClearingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
|
||
|
|
||
|
elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
|
||
|
(CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
|
||
|
|
||
|
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
|
||
|
# cruise only one step
|
||
|
if settings.stepVariable == :distance
|
||
|
s_cruising = settings.stepSize
|
||
|
elseif settings.stepVariable == time
|
||
|
s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v])
|
||
|
elseif settings.stepVariable == velocity
|
||
|
s_cruising = train[:length]/(10.0) # TODO which step size should be used?
|
||
|
end
|
||
|
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
|
||
|
|
||
|
elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached]
|
||
|
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
|
||
|
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking")
|
||
|
else
|
||
|
stateFlags[:brakingStartReached] = true
|
||
|
end
|
||
|
|
||
|
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached]
|
||
|
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, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
|
||
|
else
|
||
|
stateFlags[:brakingStartReached] = true
|
||
|
end
|
||
|
else
|
||
|
error()
|
||
|
end
|
||
|
elseif stateFlags[:tractionDeficit]
|
||
|
(CS, drivingCourse, stateFlags) = addDiminishingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
|
||
|
|
||
|
else
|
||
|
error()
|
||
|
end
|
||
|
else#if !stateFlags[:endOfCSReached] # s < s_exit
|
||
|
(CS, drivingCourse, stateFlags) = addBrakingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
|
||
|
#else
|
||
|
# error()
|
||
|
end
|
||
|
end
|
||
|
#if s == s_exit
|
||
|
# standstill
|
||
|
#end
|
||
|
|
||
|
|
||
|
# 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
|