new type Train as struct

development
Martin Scheidt 2022-05-12 16:32:15 +02:00
parent d750da80fb
commit a5868af2c5
12 changed files with 478 additions and 832 deletions

View File

@ -17,6 +17,7 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security.
* renamed TrainRun into TrainRuns * renamed TrainRun into TrainRuns
* replaced settings::Dict with type Settings as struct * replaced settings::Dict with type Settings as struct
* replaced path::Dict with type Path as struct * replaced path::Dict with type Path as struct
* replaced train::Dict with type Train as struct
* restructured examples/ and data/ in docs/ and test/ * restructured examples/ and data/ in docs/ and test/
* modified test to work with Julia Testsets and with simplier naming of input files * modified test to work with Julia Testsets and with simplier naming of input files
* renamed Validate.jl into types.jl * renamed Validate.jl into types.jl
@ -28,13 +29,14 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security.
* moved createCharacteristicSection() from characteristics.jl to types.jl * moved createCharacteristicSection() from characteristics.jl to types.jl
* changed title of include files from upper case to lower case * changed title of include files from upper case to lower case
* changed seperation of submodules into a single module with file include * changed seperation of submodules into a single module with file include
* updated test files to railtoolkit/schema (2022.04) * updated test files to railtoolkit/schema (2022.05)
### Removed ### Removed
* dependency Plots * dependency Plots
* AdditionalOutput.jl * AdditionalOutput.jl
* EnergySaving.jl * EnergySaving.jl
* test/testEnums.jl * test/testEnums.jl
* import.jl
## Version [0.8] 2022-01-20 ## Version [0.8] 2022-01-20

View File

@ -8,6 +8,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

View File

@ -9,28 +9,32 @@ __precompile__(true)
module TrainRuns module TrainRuns
## loading standard library packages ## loading standard library packages
using UUIDs, Dates using UUIDs, Dates, Statistics
## loading external packages ## loading external packages
using YAML, JSONSchema, CSV, DataFrames using YAML, JSONSchema, CSV, DataFrames
export export
## Interface ## Interface
trainrun, Path, Settings, exportToCsv trainrun, Train, Path, Settings, exportToCsv
## global variables
global g = 9.80665 # acceleration due to gravity (in m/s^2)
global μ = 0.2 # friction as constant, todo: implement as function
global Δv_air = 15.0/3.6 # coefficient for velocitiy difference between train and outdoor air (in m/s)
## include package files ## include package files
include("types.jl") include("types.jl")
include("constructors.jl") include("constructors.jl")
include("formulary.jl") include("formulary.jl")
include("calc.jl")
include("characteristics.jl") include("characteristics.jl")
include("behavior.jl") include("behavior.jl")
include("output.jl") include("output.jl")
include("import.jl")
include("export.jl") include("export.jl")
include("calc.jl")
## main function ## main function
""" """
trainrun(train::Dict, path::Path, settings::Settings) trainrun(train::Train, path::Path, settings::Settings)
Calculate the running time of a `train` on a `path`. Calculate the running time of a `train` on a `path`.
The `settings` provides the choice of models for the calculation. The `settings` provides the choice of models for the calculation.
@ -43,15 +47,7 @@ julia> trainrun(train, path)
xxx.xx # in seconds xxx.xx # in seconds
``` ```
""" """
function trainrun(trainInput::Dict, path::Path, settings=Settings()::Settings) function trainrun(train::Train, path::Path, 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 && isempty(path.poi)
train = copy(trainInput)
# check the input data
train = checkAndSetTrain!(train)
settings.outputDetail == :everything && println("The input has been checked.")
# prepare the input data # prepare the input data
movingSection = determineCharacteristics(path, train, settings) movingSection = determineCharacteristics(path, train, settings)
settings.outputDetail == :everything && println("The moving section has been prepared.") settings.outputDetail == :everything && println("The moving section has been prepared.")

View File

@ -5,7 +5,6 @@
# __copyright__ = "2020-2022" # __copyright__ = "2020-2022"
# __license__ = "ISC" # __license__ = "ISC"
## functions for calculating tractive effort and resisting forces
""" """
calculateTractiveEffort(v, tractiveEffortVelocityPairs) calculateTractiveEffort(v, tractiveEffortVelocityPairs)
@ -14,19 +13,19 @@ Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` depe
... ...
# Arguments # Arguments
- `v::AbstractFloat`: the current velocity in m/s. - `v::AbstractFloat`: the current velocity in m/s.
- `tractiveEffortVelocityPairs::Array{Array{AbstractFloat,1},1}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. - `tractiveEffortVelocityPairs::Array{}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair.
... ...
# Examples # Examples
```julia-repl ```julia-repl
julia> calculateTractiveEffort(20.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) julia> calculateTractiveEffort(20.0, [(0.0, 180000), (20.0, 100000), (40.0, 60000), (60.0, 40000), (80.0, 30000)])
100000 100000
julia> calculateTractiveEffort(30.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) julia> calculateTractiveEffort(30.0, [(0.0, 180000), (20.0, 100000), (40.0, 60000), (60.0, 40000), (80.0, 30000)])
80000 80000
``` ```
""" """
function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs) function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs::Array{})
for row in 1:length(tractiveEffortVelocityPairs) for row in 1:length(tractiveEffortVelocityPairs)
nextPair = tractiveEffortVelocityPairs[row] nextPair = tractiveEffortVelocityPairs[row]
if nextPair[1] == v if nextPair[1] == v
@ -46,19 +45,19 @@ end #function calculateTractiveEffort
""" """
calculate and return the path resistance dependend on the trains position and mass model calculate and return the path resistance dependend on the trains position and mass model
""" """
function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Train)
if massModel == :mass_point if massModel == :mass_point
pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full)
elseif massModel == :homogeneous_strip elseif massModel == :homogeneous_strip
pathResistance = 0.0 pathResistance = 0.0
s_rear = s - train[:length] # position of the rear of the train s_rear = s - train.length # position of the rear of the train
while csId > 0 && s_rear < CSs[csId][:s_exit] while csId > 0 && s_rear < CSs[csId][:s_exit]
pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train[:length] * calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train.length * calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full)
csId = csId-1 csId = csId-1
if csId == 0 if csId == 0
# TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used
return pathResistance + (CSs[1][:s_entry] - s_rear) / train[:length] * calcForceFromCoefficient(CSs[1][:r_path], train[:m_train]) return pathResistance + (CSs[1][:s_entry] - s_rear) / train.length * calcForceFromCoefficient(CSs[1][:r_path], train.m_train_full)
end #if end #if
end #while end #while
end #if end #if
@ -69,7 +68,7 @@ end #function calculatePathResistance
""" """
calculate and return tractive and resisting forces for a data point calculate and return tractive and resisting forces for a data point
""" """
function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Dict, massModel) function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Train, massModel)
# calculate resisting forces # calculate resisting forces
dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train)
dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train)
@ -81,9 +80,9 @@ function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bs
if bsType == "braking" || bsType == "coasting" if bsType == "braking" || bsType == "coasting"
dataPoint[:F_T] = 0.0 dataPoint[:F_T] = 0.0
elseif bsType == "cruising" elseif bsType == "cruising"
dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])) dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train.tractiveEffort))
else # bsType == "accelerating" || bsType == "diminishing" || 'default' else # bsType == "accelerating" || bsType == "diminishing" || 'default'
dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs]) dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort)
end end
return dataPoint return dataPoint
@ -191,7 +190,7 @@ end #function getNextPointOfInterest
## This function calculates the data points of the breakFree section. ## This function calculates the data points of the breakFree section.
# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed.
# Info: currently the values of the breakFree section will be calculated like in the accelerating section # Info: currently the values of the breakFree section will be calculated like in the accelerating section
function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
# conditions for the break free section # conditions for the break free section
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
trainIsHalting = drivingCourse[end][:v] == 0.0 trainIsHalting = drivingCourse[end][:v] == 0.0
@ -243,7 +242,7 @@ function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
s_braking = 0.0 s_braking = 0.0
else else
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
# reset state flags # reset state flags
@ -260,16 +259,16 @@ end #function addBreakFreeSection!
## This function calculates the data points of the clearing section. ## This function calculates the data points of the clearing section.
# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section.
function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
if stateFlags[:previousSpeedLimitReached] if stateFlags[:previousSpeedLimitReached]
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
ignoreBraking = true ignoreBraking = true
s_braking = 0.0 s_braking = 0.0
else else
ignoreBraking = false ignoreBraking = false
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
s_clearing = min(CS[:s_exit]-drivingCourse[end][:s]-s_braking, currentSpeedLimit[:s_end] - drivingCourse[end][:s]) s_clearing = min(CS[:s_exit]-drivingCourse[end][:s]-s_braking, currentSpeedLimit[:s_end] - drivingCourse[end][:s])
@ -282,7 +281,7 @@ function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id]) error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id])
end end
#stateFlags[:previousSpeedLimitReached] = false #stateFlags[:previousSpeedLimitReached] = false
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v]
else else
stateFlags[:error] = true stateFlags[:error] = true
@ -293,9 +292,9 @@ end #function addClearingSection
## This function calculates the data points of the accelerating section. ## This function calculates the data points of the accelerating section.
# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section
function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
#function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Train, CSs::Vector{Dict}, ignoreBraking::Bool)
#=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Train)
CSs = movingSection[:characteristicSections] CSs = movingSection[:characteristicSections]
CS = CSs[csId] CS = CSs[csId]
drivingCourse = movingSection[:drivingCourse]=# drivingCourse = movingSection[:drivingCourse]=#
@ -307,7 +306,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
s_braking = 0.0 s_braking = 0.0
else else
ignoreBraking = false ignoreBraking = false
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
# conditions for the accelerating section # conditions for the accelerating section
@ -322,7 +321,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i])
drivingCourse[end][:behavior] = BS[:type] drivingCourse[end][:behavior] = BS[:type]
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v]
speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit] speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit]
#speedLimitReached = drivingCourse[end][:v] > currentSpeedLimit[:v] #speedLimitReached = drivingCourse[end][:v] > currentSpeedLimit[:v]
@ -334,18 +333,18 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
if !ignoreBraking if !ignoreBraking
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached
# 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit
if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] if drivingCourse[end][:s] >= currentSpeedLimit[:s_end]
# could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
end end
# acceleration (in m/s^2): # acceleration (in m/s^2):
drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train)
# create the next data point # create the next data point
push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id]))
@ -356,7 +355,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
# conditions for the next while cycle # conditions for the next while cycle
if !ignoreBraking if !ignoreBraking
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit]
speedLimitReached = drivingCourse[end][:v] > CS[:v_limit] speedLimitReached = drivingCourse[end][:v] > CS[:v_limit]
@ -539,7 +538,7 @@ end #function addAcceleratingSection!
## This function calculates the data points of the cruising section. ## This function calculates the data points of the cruising section.
# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed.
function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Dict, CSs::Vector{Dict}, cruisingType::String) function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Train, CSs::Vector{Dict}, cruisingType::String)
trainIsClearing = cruisingType == "clearing" trainIsClearing = cruisingType == "clearing"
trainIsBrakingDownhill = cruisingType == "downhillBraking" trainIsBrakingDownhill = cruisingType == "downhillBraking"
@ -555,11 +554,11 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
s_braking = 0.0 s_braking = 0.0
else else
ignoreBraking = false ignoreBraking = false
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
# conditions for cruising section # conditions for cruising section
#s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached]
speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak]
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
@ -584,7 +583,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
if settings.massModel == :homogeneous_strip && CS[:id] > 1 if settings.massModel == :homogeneous_strip && CS[:id] > 1
# conditions for cruising section # conditions for cruising section
trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train.length
targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising
resistingForceNegative = drivingCourse[end][:F_R] < 0.0 resistingForceNegative = drivingCourse[end][:F_R] < 0.0
# targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak] # targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak]
@ -598,7 +597,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used
# 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train.length && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R]
# the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS # the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS
#TODO: maybe just consider former CS with different path resistance? #TODO: maybe just consider former CS with different path resistance?
# tractive effort (in N): # tractive effort (in N):
@ -617,7 +616,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
if settings.stepVariable == :distance || settings.stepVariable == time if settings.stepVariable == :distance || settings.stepVariable == time
push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id]))
else else
push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? push!(drivingCourse, moveAStep(drivingCourse[end], position, train.length/(10.0^cycle), CS[:id])) # TODO which step size should be used?
end end
drivingCourse[end][:behavior] = BS[:type] drivingCourse[end][:behavior] = BS[:type]
push!(BS[:dataPoints], drivingCourse[end][:i]) push!(BS[:dataPoints], drivingCourse[end][:i])
@ -635,7 +634,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising
trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train.length
resistingForceNegative = drivingCourse[end][:F_R] < 0.0 resistingForceNegative = drivingCourse[end][:F_R] < 0.0
end #while end #while
@ -657,7 +656,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
currentStepSize = settings.stepSize / 10.0^cycle currentStepSize = settings.stepSize / 10.0^cycle
end end
elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train[:length])) elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train.length))
if settings.stepVariable == :distance if settings.stepVariable == :distance
currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s]
else else
@ -667,7 +666,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit]
break break
elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] elseif drivingCourse[end][:s] >= CS[:s_entry] + train.length
break break
elseif drivingCourse[end][:s] == nextPointOfInterest[1] elseif drivingCourse[end][:s] == nextPointOfInterest[1]
@ -782,12 +781,12 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
# set state flags # set state flags
stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit] stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit]
if !ignoreBraking if !ignoreBraking
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit] stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit]
stateFlags[:tractionDeficit] = tractionDeficit stateFlags[:tractionDeficit] = tractionDeficit
stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0 stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v]
stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative))) stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative)))
@ -796,7 +795,7 @@ end #function addCruisingSection!
## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower ## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower
function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel)
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
@ -804,14 +803,14 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
s_braking = 0.0 s_braking = 0.0
else else
ignoreBraking = false ignoreBraking = false
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
# conditions for diminishing section # conditions for diminishing section
targetSpeedReached = drivingCourse[end][:v] <= 0.0 targetSpeedReached = drivingCourse[end][:v] <= 0.0
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit] tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit]
#s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached]
# use the conditions for the diminishing section # use the conditions for the diminishing section
@ -828,7 +827,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached
# 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end # 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end
# acceleration (in m/s^2): # acceleration (in m/s^2):
drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train)
# create the next data point # create the next data point
push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id]))
@ -839,7 +838,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
# conditions for the next while cycle # conditions for the next while cycle
if !ignoreBraking if !ignoreBraking
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
end end
brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit]
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
@ -983,7 +982,7 @@ end #function addDiminishingSection!
## This function calculates the data points of the coasting section. ## This function calculates the data points of the coasting section.
# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section
function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
# TODO: if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept # TODO: if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept
# with getCurrentSpeedLimit # with getCurrentSpeedLimit
@ -991,7 +990,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit]
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached]
# use the conditions for the coasting section # use the conditions for the coasting section
@ -1011,7 +1010,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel)
# acceleration (in m/s^2): # acceleration (in m/s^2):
drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train)
# create the next data point # create the next data point
push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id]))
@ -1019,7 +1018,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
push!(BS[:dataPoints], drivingCourse[end][:i]) push!(BS[:dataPoints], drivingCourse[end][:i])
# conditions for the next while cycle # conditions for the next while cycle
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit]
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak]
@ -1146,7 +1145,7 @@ end #function addCoastingSection!
## This function calculates the data points of the braking section. ## This function calculates the data points of the braking section.
# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. # 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}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict})
# conditions for braking section # conditions for braking section
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit]
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
@ -1168,7 +1167,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel)
# acceleration (in m/s^2): # acceleration (in m/s^2):
drivingCourse[end][:a] = train[:a_braking] drivingCourse[end][:a] = train.a_braking
# TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s]) # TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s])
if settings.stepVariable == :distance && ((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 if settings.stepVariable == :distance && ((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
@ -1289,7 +1288,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
end # else: return the characteristic section without a braking section end # else: return the characteristic section without a braking section
# set state flags # set state flags
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v]
stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit]
stateFlags[:endOfCSReached] = endOfCSReached stateFlags[:endOfCSReached] = endOfCSReached
@ -1303,7 +1302,7 @@ end #function addBrakingSection!
## This function calculates the data point of the standstill. ## This function calculates the data point of the standstill.
# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed.
function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Train, CSs::Vector{Dict})
if drivingCourse[end][:v] == 0.0 if drivingCourse[end][:v] == 0.0
BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i])
merge!(BS, Dict(:length => 0.0, # total length (in m) merge!(BS, Dict(:length => 0.0, # total length (in m)
@ -1347,8 +1346,8 @@ function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target)
# calculate other values # calculate other values
previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs]) previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs])
# # TODO: just for testing # # TODO: just for testing
# if previousPoint[:a]<train[:a_braking] || previousPoint[:a]>=0.0 # if previousPoint[:a]<train.a_braking || previousPoint[:a]>=0.0
# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train[:a_braking]) # println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train.a_braking)
# end # end
currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s) currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s)
currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s) currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s)

View File

@ -8,7 +8,7 @@
# Calculate the running time of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. # Calculate the running time of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`.
# calculate a train run focussing on using the minimum possible running time # calculate a train run focussing on using the minimum possible running time
function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Train)
CSs::Vector{Dict} = movingSection[:characteristicSections] CSs::Vector{Dict} = movingSection[:characteristicSections]
if settings.massModel == :homogeneous_strip && settings.stepVariable == speed if settings.massModel == :homogeneous_strip && settings.stepVariable == speed
@ -32,7 +32,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
end end
# determine the different flags for switching between the states for creatinge moving phases # 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]) 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) calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N)
previousSpeedLimitReached = false previousSpeedLimitReached = false
@ -64,12 +64,12 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
elseif settings.stepVariable == time elseif settings.stepVariable == time
s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v]) s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v])
elseif settings.stepVariable == velocity elseif settings.stepVariable == velocity
s_cruising = train[:length]/(10.0) # TODO which step size should be used? s_cruising = train.length/(10.0) # TODO which step size should be used?
end end
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached] elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached]
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0 if s_cruising > 0.0
@ -79,7 +79,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
end end
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached] elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached]
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0 # TODO: define a minimum cruising length? if s_cruising > 0.0 # TODO: define a minimum cruising length?

View File

@ -6,9 +6,9 @@
# __license__ = "ISC" # __license__ = "ISC"
## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior ## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior
function determineCharacteristics(path::Path, train::Dict, settings::Settings) function determineCharacteristics(path::Path, train::Train, settings::Settings)
movingSection = createMovingSection(path, train[:v_limit], train[:length]) movingSection = createMovingSection(path, train.v_limit, train.length)
movingSection = secureBrakingBehavior!(movingSection, train[:a_braking]) movingSection = secureBrakingBehavior!(movingSection, train.a_braking)
movingSection = secureAcceleratingBehavior!(movingSection, settings, train) movingSection = secureAcceleratingBehavior!(movingSection, settings, train)
#movingSection = secureCruisingBehavior!(movingSection, settings, train) #movingSection = secureCruisingBehavior!(movingSection, settings, train)
@ -45,7 +45,7 @@ function secureBrakingBehavior!(movingSection::Dict, a_braking::Real)
end #function secureBrakingBehavior! end #function secureBrakingBehavior!
## define the intersection velocities between the characterisitc sections to secure accelerating behavior ## define the intersection velocities between the characterisitc sections to secure accelerating behavior
function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Dict) function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Train)
# this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards
CSs = movingSection[:characteristicSections] CSs = movingSection[:characteristicSections]
@ -82,7 +82,7 @@ function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, tr
(CS, acceleratingCourse, stateFlags) = addClearingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the train is not allowed to accelerate because of a previous speed limit (CS, acceleratingCourse, stateFlags) = addClearingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the train is not allowed to accelerate because of a previous speed limit
end end
else else
if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train[:length] if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train.length
break break
else else
(CS, acceleratingCourse, stateFlags) = addDiminishingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort (CS, acceleratingCourse, stateFlags) = addDiminishingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort
@ -112,7 +112,7 @@ end #function secureAcceleratingBehavior!
#= #=
## define the intersection velocities between the characterisitc sections to secure cruising behavior ## define the intersection velocities between the characterisitc sections to secure cruising behavior
function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Dict) function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Train)
# limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak # limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak
CSs = movingSection[:characteristicSections] CSs = movingSection[:characteristicSections]
@ -147,7 +147,7 @@ function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train:
(CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking")
end end
else else
if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train[:length] if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train.length
break break
else else
(CS, cruisingCourse, stateFlags) = addDiminishingSection!(CS, cruisingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort (CS, cruisingCourse, stateFlags) = addDiminishingSection!(CS, cruisingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort

View File

@ -1,7 +1,7 @@
#!/usr/bin/env julia #!/usr/bin/env julia
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
# __julia-version__ = 1.7.2 # __julia-version__ = 1.7.2
# __author__ = "Martin Scheidt" # __author__ = "Martin Scheidt, Max Kannenberg"
# __copyright__ = "2022" # __copyright__ = "2022"
# __license__ = "ISC" # __license__ = "ISC"
@ -77,7 +77,7 @@ function Settings(file="DEFAULT")
settings = Dict() settings = Dict()
end end
## set the variables if they exist in "settings" ## set the variables in "settings"
haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing
haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing
haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing
@ -89,8 +89,7 @@ function Settings(file="DEFAULT")
Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir)
end #function Settings() # constructor end #function Settings() # outer constructor
""" """
Path(file, type = :YAML) Path(file, type = :YAML)
@ -227,7 +226,7 @@ function Path(file, type = :YAML)
end end
path = paths[1] path = paths[1]
## set the variables if they exist in "settings" ## set the variables in "path"
# required # required
name = path["name"] name = path["name"]
id = path["id"] id = path["id"]
@ -275,7 +274,345 @@ function Path(file, type = :YAML)
Path(name, id, uuid, poi, sections) Path(name, id, uuid, poi, sections)
end #function Path() # constructor end #function Path() # outer constructor
"""
Train(file, type = :YAML)
Train is a datastruture for calculation context.
The function Train() will create a train to use in calculations.
Supported formats for the YAML files are: railtoolkit/schema (2022.05)
# Example
```jldoctest
julia> my_train = Train("file.yaml") # will generate a train from a YAML file.
Train(variables)
```
"""
function Train(file, type = :YAML)
## default values
name = "" #
id = "" #
uuid = UUIDs.uuid4() #
length = 0 # in meter
m_train_full = 0 # in kilogram
m_train_empty = 0 # in kilogram
m_loco = 0 # in kilogram
m_td = 0 # in kilogram
m_tc = 0 # in kilogram
m_w = 0 # in kilogram
ξ_train = 1.08 # rotation mass factor, source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Zug, überschlägliche Berechnung"
ξ_loco = 1.09 # rotation mass factor
ξ_cars = 1.06 # rotation mass factor
transportType = :freight # "freight" or "passenger" for resistance calculation
v_limit = 140 # in m/s (default 504 km/h)
a_braking = 0 # in m/s^2, todo: implement as function
f_Rtd0 = 0 # coefficient for basic resistance due to the traction units driving axles (in ‰)
f_Rtc0 = 0 # coefficient for basic resistance due to the traction units carring axles (in ‰)
F_Rt2 = 3000 # coefficient for air resistance of the traction units (in N)
f_Rw0 = 0 # coefficient for the consists basic resistance (in ‰)
f_Rw1 = 0 # coefficient for the consists resistance to rolling (in ‰)
f_Rw2 = 0 # coefficient fo the consistsr air resistance (in ‰)
F_v_pairs = [] # [v in m/s, F_T in N]
## load from file
if type == :YAML
data = YAML.load(open(file))
if data["schema"] != "https://railtoolkit.org/schema/rolling-stock.json"
error("Could not load path file '$file'.\n
YAML format is not recognized.
Currently supported: railtoolkit/schema/rolling-stock (2022.05)")
end
if data["schema_version"] != "2022.05"
error("Could not load path file '$file'.\n
YAML format is not recognized.
Currently supported: railtoolkit/schema/rolling-stock (2022.05)")
end
## JSON schema for YAML-file validation
railtoolkit_schema = Schema("""{
"required": [ "schema", "schema_version" ],
"anyOf": [
{"required": [ "trains" ] },
{"required": [ "vehicles" ] }
],
"properties": {
"schema": {
"description": "Identifier of the schema",
"enum": [ "https://railtoolkit.org/schema/rolling-stock.json" ]
},
"schema_version": {
"description": "Version of the schema",
"type": "string",
"pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]"
},
"trains": {
"type": "array",
"minItems": 1,
"items": {
"required": [ "name", "id", "formation" ],
"type": "object",
"properties": {
"id": {
"description": "Identifier of the train",
"type": "string"
},
"name": {
"description": "Name of the train",
"type": "string"
},
"UUID": {
"description": "The unique identifier for a train",
"type": "string",
"format": "uuid"
},
"formation": {
"description": "Collection of vehicles that form the train",
"type": "array",
"minItems": 1,
"uniqueItems": false,
"items": {
"type": "string"
}
}
}
}
},
"vehicles": {
"type": "array",
"minItems": 1,
"items": {
"required": [ "name", "id", "vehicle_type", "length", "mass" ],
"type": "object",
"properties": {
"air_resistance": {
"description": "coefficient for air resistance in permil",
"type": "number",
"exclusiveMinimum": 0
},
"base_resistance": {
"description": "coefficient for basic resistance in permil",
"type": "number",
"exclusiveMinimum": 0
},
"id": {
"description": "Identifier of the vehicle",
"type": "string"
},
"length": {
"description": "The length of the vehicle in meter",
"type": "number",
"exclusiveMinimum": 0
},
"load_limit": {
"description": "The maximum permitted load of the vehicle in metric ton",
"type": "number",
"exclusiveMinimum": 0
},
"mass_traction": {
"description": "The mass on the powered axles of the vehicle in metric ton",
"type": "number",
"exclusiveMinimum": 0
},
"mass": {
"description": "The empty mass of the vehicle in metric ton",
"type": "number",
"exclusiveMinimum": 0
},
"name": {
"description": "Name of the vehicle",
"type": "string"
},
"picture": {
"description": "A URI with a picture for humans",
"type": "string",
"format": "uri"
},
"power_type": {
"description": "Type of propulsion",
"enum": [ "diesel", "electric", "steam" ]
},
"rolling_resistance": {
"description": "coefficient for resistance of rolling axles in permil",
"type": "number",
"exclusiveMinimum": 0
},
"rotation_mass": {
"description": "Factor for rotating mass; >= 1",
"type": "number",
"minimum": 1
},
"speed_limit": {
"description": "Maximum permitted speed in kilometers per hour",
"type": "number",
"exclusiveMinimum": 0
},
"tractive_effort": {
"description": "Tractive effort as pairs of speed in kilometers per hour and tractive force in newton",
"type": "array",
"minItems": 3,
"uniqueItems": true,
"items": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"uniqueItems": true,
"items": {
"type": "number",
"minimum": 0
}
}
},
"UUID": {
"description": "The unique identifier for a vehicle",
"type": "string",
"format": "uuid"
},
"vehicle_type": {
"description": "Type of vehicle",
"enum": [ "traction unit", "freight", "passenger", "multiple unit" ]
}
}
}
}
}
}""")
try
validate(railtoolkit_schema, data)
catch err
error("Could not load path file '$file'.\n
YAML format is not recognized.
Currently supported: railtoolkit/schema/rolling-stock (2022.05)")
end
else
error("Unknown file type '$type'")
end #if type
trains = data["trains"]
Base.length(trains) > 1 ? println("WARNING: the loaded file contains more than one train. Using only the first!") : nothing
Base.length(trains) == 0 ? error("No train present in file '$file'") : nothing
train = trains[1]
used_vehicles = unique(train["formation"])
included_vehicles = []
for vehicle in data["vehicles"]
push!(included_vehicles,vehicle["id"])
end
## test if all vehicles of the formation are avilable
for vehicle in used_vehicles
vehicle included_vehicles ? error("'$vehicle' is not present in '$file'") : nothing
end
## gather the count of vehicles and usage in the formation
vehicles = NamedTuple[]
for vehicle in data["vehicles"]
if vehicle["id"] in used_vehicles
n = count(==(vehicle["id"]),train["formation"])
type = vehicle["vehicle_type"]
type == "traction unit" || type == "multiple unit" ? propulsion = true : propulsion = false
type == "passenger" || type == "multiple unit" ? transportType = :passenger : nothing
push!(vehicles, (data=vehicle, n=n, propulsion=propulsion) )
end
end
## set the variables in "train"
name = train["name"]
id = train["id"]
haskey(train, "UUID") ? uuid = parse(UUID, train["UUID"] ) : nothing
transportType == :freight ? a_braking = -0.225 : a_braking = -0.375 # set a default a_braking value depending on the train type
## set the variables for all vehicles
for vehicle in vehicles
length += vehicle.data["length"] * vehicle.n
m_train_full += vehicle.data["mass"] * vehicle.n * 1000 # in kg
m_train_empty += vehicle.data["mass"] * vehicle.n * 1000 # in kg
haskey(vehicle.data, "load_limit") ?
m_train_full += vehicle.data["load_limit"] * vehicle.n * 1000 : # in kg
nothing
haskey(vehicle.data, "speed_limit") ?
v_limit > vehicle.data["speed_limit"]/3.6 ? v_limit = vehicle.data["speed_limit"]/3.6 : nothing :
nothing
end
## divide vehicles in propulsion and non-propulsion
loco = []
for i in 1:Base.length(vehicles)
if vehicles[i].propulsion
push!(loco, vehicles[i])
deleteat!(vehicles, i)
end
end
Base.length(loco) > 1 ? println("WARNING: the loaded file contains more than one traction unit or multiple unit. Using only the first!") : nothing
loco[1].n > 1 ? println("WARNING: the loaded file contains more than one traction unit or multiple unit. Using only one!") : nothing
Base.length(loco) == 0 ? error("No traction unit or multiple unit present in file '$file'") : nothing
loco = loco[1].data
cars = vehicles
## set the variables for locos
m_loco= loco["mass"] * 1000
haskey(loco, "a_braking") ? a_braking = loco["a_braking"] : nothing
haskey(loco, "base_resistance") ? f_Rtd0 = loco["base_resistance"] : nothing
haskey(loco, "rolling_resistance") ? f_Rtc0 = loco["rolling_resistance"] : nothing
haskey(loco, "air_resistance") ? F_Rt2 = loco["air_resistance"] * g * m_loco : nothing
haskey(loco, "mass_traction") ? m_td = loco["mass_traction"] * 1000 : m_td = m_t
haskey(loco, "rotation_mass") ? ξ_loco = loco["rotation_mass"] : nothing
m_tc = m_loco- m_td
haskey(loco, "tractive_effort") ? F_v_pairs = loco["tractive_effort"] : F_v_pairs = [ [0.0, m_td * g * μ],[v_limit*3.6, m_td * g * μ] ]
F_v_pairs = reduce(hcat,F_v_pairs)' # convert to matrix
F_v_pairs[:,1] ./= 3.6 # convert km/h to m/s
F_v_pairs = tuple.(eachcol(F_v_pairs)...) # convert each row to tuples
## set the variables for cars
if !isempty(cars)
resis_base = []
resis_roll = []
resis_air = []
rotMassFac = []
for car in cars
haskey(car.data, "base_resistance") ?
append!(resis_base,repeat([car.data["base_resistance"]],car.n)) :
append!(resis_base,repeat([f_Rw0],car.n))
haskey(car.data, "rolling_resistance") ?
append!(resis_roll,repeat([car.data["rolling_resistance"]],car.n)) :
append!(resis_roll,repeat([f_Rw1],car.n))
haskey(car.data, "air_resistance") ?
append!(resis_air,repeat([car.data["air_resistance"]],car.n)) :
append!(resis_air, repeat([f_Rw2],car.n))
haskey(car.data, "rotation_mass") ?
append!(rotMassFac,repeat([(car.data["rotation_mass"],car.data["mass"])],car.n)) :
append!(rotMassFac,repeat([(ξ_cars ,car.data["mass"])],car.n))
m_w += car.data["mass"] * car.n * 1000 # in kg
end
f_Rw0 = Statistics.mean(resis_base)
f_Rw1 = Statistics.mean(resis_roll)
f_Rw2 = Statistics.mean(resis_air)
carRotMass = 0
for elem in rotMassFac
carRotMass += elem[1]*elem[2] * 1000 # in kg
end
ξ_cars = carRotMass/m_w
ξ_train = (ξ_loco * m_loco+ carRotMass)/m_train_empty
else
ξ_cars = 0
ξ_train = ξ_loco
end
Train(
name, id, uuid, length,
m_train_full, m_td, m_tc, m_w,
ξ_train, ξ_loco, ξ_cars,
transportType, v_limit,
a_braking,
f_Rtd0, f_Rtc0, F_Rt2, f_Rw0, f_Rw1, f_Rw2,
F_v_pairs
)
end #function Train() # outer constructor
## create a moving section containing characteristic sections ## create a moving section containing characteristic sections
function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real)

View File

@ -30,7 +30,6 @@
approxLevel = 6 approxLevel = 6
v00 = 100/3.6 # velocity factor (in m/s) v00 = 100/3.6 # velocity factor (in m/s)
g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2
## calculate forces ## calculate forces
@ -43,7 +42,7 @@ Calculate the vehicle resistance for the traction unit of the `train` dependend
... ...
# Arguments # Arguments
- `v::AbstractFloat`: the current velocity in m/s. - `v::AbstractFloat`: the current velocity in m/s.
- `train::Dict`: ? ? ? - `train::Train`: ? ? ?
... ...
# Examples # Examples
@ -52,36 +51,34 @@ julia> calcTractionUnitResistance(30.0, ? ? ?)
? ? ? ? ? ?
``` ```
""" """
function calcTractionUnitResistance(v::AbstractFloat, train::Dict) function calcTractionUnitResistance(v::AbstractFloat, train::Train)
# equation is based on [Wende:2003, page 151] # equation is based on [Wende:2003, page 151]
f_Rtd0 = train[:f_Rtd0] # coefficient for basic resistance due to the traction units driving axles (in ‰) f_Rtd0 = train.f_Rtd0 # coefficient for basic resistance due to the traction units driving axles (in ‰)
f_Rtc0 = train[:f_Rtc0] # coefficient for basic resistance due to the traction units carring axles (in ‰) f_Rtc0 = train.f_Rtc0 # coefficient for basic resistance due to the traction units carring axles (in ‰)
F_Rt2 = train[:F_Rt2] # coefficient for air resistance of the traction units (in N) F_Rt2 = train.F_Rt2 # coefficient for air resistance of the traction units (in N)
m_td = train[:m_td] # mass on the traction unit's driving axles (in kg) m_td = train.m_td # mass on the traction unit's driving axles (in kg)
m_tc = train[:m_tc] # mass on the traction unit's carrying axles (in kg) m_tc = train.m_tc # mass on the traction unit's carrying axles (in kg)
Δv_t = train[:Δv_t] # coefficient for velocitiy difference between traction unit and outdoor air (in m/s)
F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_air) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰
# TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_air) /v00)^2 # vehicle resistance of the traction unit (in N)
return F_R_tractionUnit return F_R_tractionUnit
#TODO: same variable name like in the rest of the tool? return R_traction #TODO: same variable name like in the rest of the tool? return R_traction
#TODO: just one line? return train[:f_Rtd0]/1000*train[:m_td]*g+train[:f_Rtc0]/1000*train[:m_tc]*g+train[:F_Rt2]*((v+train[:Δv_t])/v00)^2 # /1000 because of the unit ‰ #TODO: just one line? return train.f_Rtd0/1000*train.m_td*g+train.f_Rtc0/1000*train.m_tc*g+train.F_Rt2*((v+train.Δv_air)/v00)^2 # /1000 because of the unit ‰
end #function calcTractionUnitResistance end #function calcTractionUnitResistance
""" """
TODO TODO
calculate and return the wagons vehicle resistance dependend on the velocity calculate and return the wagons vehicle resistance dependend on the velocity
""" """
function calcWagonsResistance(v::AbstractFloat, train::Dict) function calcWagonsResistance(v::AbstractFloat, train::Train)
# equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135) # equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135)
f_Rw0 = train[:f_Rw0] # coefficient for basic resistance of the set of wagons (consist) (in ‰) f_Rw0 = train.f_Rw0 # coefficient for basic resistance of the set of wagons (consist) (in ‰)
f_Rw1 = train[:f_Rw1] # coefficient for the consists resistance to rolling (in ‰) f_Rw1 = train.f_Rw1 # coefficient for the consists resistance to rolling (in ‰)
f_Rw2 = train[:f_Rw2] # coefficient fo the consistsr air resistance (in ‰) f_Rw2 = train.f_Rw2 # coefficient fo the consistsr air resistance (in ‰)
m_w = train[:m_w] # mass of the set of wagons (consist) (in kg) m_w = train.m_w # mass of the set of wagons (consist) (in kg)
Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s)
F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_w) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_air) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰
# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_w) /v00)^2 # vehicle resistance of the wagons (in N) # TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_air) /v00)^2 # vehicle resistance of the wagons (in N)
return F_R_wagons return F_R_wagons
end #function calcWagonsResistance end #function calcWagonsResistance

View File

@ -1,35 +0,0 @@
#!/usr/bin/env julia
# -*- coding: UTF-8 -*-
# __julia-version__ = 1.7.2
# __author__ = "Max Kannenberg"
# __copyright__ = "2020-2022"
# __license__ = "ISC"
"""
Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them.
"""
function importYamlFiles(trainDirectory::String, pathDirectory::String)
train = importFromYaml(:train, trainDirectory)
path = importFromYaml(:path, pathDirectory)
return (train, path)
end #function importYamlFiles
"""
Read the train information from a YAML file, save it in a Dict and return it.
"""
function importFromYaml(dataType::Symbol, directory::String)
dataSet = String(dataType)
data = YAML.load(open(directory))
if collect(keys(data))[1] != dataSet
error("ERROR at reading the ", dataSet, " yaml file: The data set is called ", collect(keys(data))[1]," and not ", dataSet, ".")
end
dataKeys = collect(keys(data[dataSet]))
dataKeys = collect(keys(data[dataSet]))
dataValues = collect(values(data[dataSet]))
dictionary = Dict()
for number in 1:length(dataKeys)
merge!(dictionary, Dict(Symbol(dataKeys[number]) => dataValues[number]))
end
return dictionary
end # function importFromYaml

View File

@ -5,7 +5,7 @@
# __copyright__ = "2020-2022" # __copyright__ = "2020-2022"
# __license__ = "ISC" # __license__ = "ISC"
function createOutput(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) function createOutput(train::Train, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict})
if settings.outputDetail == :running_time if settings.outputDetail == :running_time
output = movingSection[:t] # TODO: or use drivingCourse[end][:t] output = movingSection[:t] # TODO: or use drivingCourse[end][:t]
@ -71,7 +71,7 @@ function createOutput(train::Dict, settings::Settings, path::Path, movingSection
end end
#= #=
function createOutputDict(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) function createOutputDict(train::Train, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict})
outputDict = Dict{Symbol,Any}() outputDict = Dict{Symbol,Any}()
merge!(outputDict, Dict(:train => train, :path => path, :settings => settings)) merge!(outputDict, Dict(:train => train, :path => path, :settings => settings))

View File

@ -1,7 +1,7 @@
#!/usr/bin/env julia #!/usr/bin/env julia
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
# __julia-version__ = 1.7.2 # __julia-version__ = 1.7.2
# __author__ = "Max Kannenberg, Martin Scheidt" # __author__ = "Martin Scheidt, Max Kannenberg"
# __copyright__ = "2022" # __copyright__ = "2022"
# __license__ = "ISC" # __license__ = "ISC"
@ -28,686 +28,35 @@ struct Path
end #struct Path end #struct Path
""" struct Train
Read the train information from a YAML file, save it in a train Dict and return it.
""" name::String # a name or description of the train
function checkAndSetTrain!(train::Dict) id::String # a short string as identifier
# check train information from input dictionary uuid::UUID # a unique identifier
length::Real # train length in meter
checkAndSetString!(train, "train", :name, "") # train's name m_train_full::Real # mass of the full loaded train in kilogram
# add train's identifier if not existing m_td::Real # mass on driving axles of the traction unit in kilogram
if !(haskey(train, :id) && train[:id]!=nothing) m_tc::Real # mass on the traction unit's carrying axles in kilogram
merge!(train, Dict(:id =>1)) m_w::Real # mass of the set of wagons/cars/consist in kilogram
end ξ_train::Real # rotation mass factor
checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight" ξ_loco::Real # rotation mass factor
ξ_cars::Real # rotation mass factor
checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) transportType::Symbol # ":freight" or ":passenger" for resistance calculation
# TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) v_limit::Real # in m/s
a_braking::Real # in m/s^2
checkAndSetSpeedLimit!(train) # train's speed limit (in m/s)
checkAndSetBrakingAcceleration!(train) # a_braking # coefficients for the vehicle resistance
# for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_air)/v00)^2)
checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg) f_Rtd0::Real # coefficient for basic resistance due to the traction units driving axles (in ‰)
checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg) f_Rtc0::Real # coefficient for basic resistance due to the traction units carring axles (in ‰)
checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg) F_Rt2::Real # coefficient for air resistance of the traction units (in N)
checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg) # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_air)/v00)^2))
checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg) f_Rw0::Real # coefficient for the consists basic resistance (in ‰)
if train[:m_train] <= 0.0 f_Rw1::Real # coefficient for the consists resistance to rolling (in ‰)
error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.") f_Rw2::Real # coefficient fo the consistsr air resistance (in ‰)
end
# tractive effort as pairs of speed and tractive effort
checkAndSetRotationMassFactors!(train) tractiveEffort::Vector{Tuple{Real, Real}} # [v in m/s, F_T in N]
checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort
end #struct Train
# coefficients for the vehicle resistance of the traction unit
checkAndSetRealNumber!(train, "train", :Δv_t, "m/s", 15.0/3.6) # coefficient for velocitiy difference between traction unit and outdoor air (in m/s)
checkAndSetPositiveNumber!(train, "train", :f_Rtd0, "", 0.0) # coefficient for basic resistance due to the traction units driving axles (in ‰)
checkAndSetPositiveNumber!(train, "train", :f_Rtc0, "", 0.0) # coefficient for basic resistance due to the traction units carring axles (in ‰)
checkAndSetPositiveNumber!(train, "train", :F_Rt2, "N", 0.0) # coefficient for air resistance of the traction units (in N)
# coefficients for the vehicle resistance of the set of wagons (consist)
checkAndSetRealNumber!(train, "train", :Δv_w, "m/s", getDefault_Δv_w(train[:type])) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s)
checkAndSetPositiveNumber!(train, "train", :f_Rw0, "", 0.0) # coefficient for basic resistance of the set of wagons (consist) (in ‰)
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 ‰)
# inform the user about keys of the input dictionary that are not used in this tool
usedKeys = [:name, :id, :type,
:length, :l_train, :v_limit, :v_limit_kmh, :a_braking,
:m_train, :m_t, :m_td, :m_tc, :m_w,
:ξ_train, :ξ_t, :ξ_w, :rotationMassFactor_train, :rotationMassFactor_t, :rotationMassFactor_w,
:tractiveEffortVelocityPairs, :F_T_pairs, :F_T_pairs_kmh,
:f_Rtd0, :f_Rtc0, :F_Rt2, :Δv_t,
:f_Rw0, :f_Rw1, :f_Rw2, :Δv_w]
informAboutUnusedKeys(collect(keys(train)), usedKeys::Vector{Symbol}, "train")
return train
end #function checkAndSetTrain!
function checkAndSetPath!(path::Path)
# check path information from input dictionary
checkAndSetString!(path, "path", :name, "")
# TODO checkId ? path[:id] # path identifier
checkAndSetSections!(path)
checkAndSetPOIs!(path)
# inform the user about keys of the input dictionary that are not used in this tool
usedKeys = [:name,
:sections, :sectionStarts, :sectionStarts_kmh,
:pointsOfInterest]
informAboutUnusedKeys(collect(keys(path)), usedKeys::Vector{Symbol}, "path")
return path
end # function checkAndSetPath!
function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::Bool)
if haskey(dictionary,key) && dictionary[key]!=nothing
if typeof(dictionary[key]) != Bool
error("ERROR at checking the input for the ",dictionaryType,": The value of the key ",String(key)," is not correct. The value has to be of type Bool.")
end
else
merge!(dictionary, Dict(key => defaultValue))
defaultValue && println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.")
end
return dictionary
end #function checkAndSetBool!
function checkAndSetPositiveNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real)
if haskey(dictionary,key) && dictionary[key]!=nothing
if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.")
end
else
merge!(dictionary, Dict(key => default))
println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." )
end
return dictionary
end #function checkAndSetPositiveNumber!
# first method without a default value
function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String)
mainKey_temp = -1.0
alternativeKey_temp = -1.0
if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing
if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0
mainKey_temp = dictionary[mainKey]
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.")
end
end
if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing
if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0
alternativeKey_temp = dictionary[alternativeKey]
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.")
end
else
delete!(dictionary, alternativeKey)
end
if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0
difference = abs(mainKey_temp - alternativeKey_temp)
if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ?
delete!(dictionary, alternativeKey)
println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." )
end
elseif mainKey_temp >= 0.0
# do nothing
elseif alternativeKey_temp >= 0.0
merge!(dictionary, Dict(mainKey => alternativeKey_temp))
else
# do nothing
end
return dictionary
end #function checkAndSetPositiveNumberWithDifferentNames!
# second method with a default value
function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String, default::Real)
mainKey_temp = -1.0
alternativeKey_temp = -1.0
if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing
if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0
mainKey_temp = dictionary[mainKey]
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.")
end
end
if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing
if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0
alternativeKey_temp = dictionary[alternativeKey]
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.")
end
else
delete!(dictionary, alternativeKey)
end
if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0
difference = abs(mainKey_temp - alternativeKey_temp)
if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ?
delete!(dictionary, alternativeKey)
println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." )
end
elseif mainKey_temp >= 0.0
# do nothing
elseif alternativeKey_temp >= 0.0
merge!(dictionary, Dict(mainKey => alternativeKey_temp))
else
# set a default value
merge!(dictionary, Dict(mainKey, default))
println("INFO at checking the input for the ",dictionaryType,": The key ",mainKey," or its value is missing. Therefore the value ",String(mainKey),"=",default," ",unit," is used." )
end
return dictionary
end #function checkAndSetPositiveNumberWithDifferentNames!
function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real)
if haskey(dictionary,key) && dictionary[key]!=nothing
if typeof(dictionary[key]) <: Real
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.")
end
else
merge!(dictionary, Dict(key => default))
println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." )
end
return dictionary
end #function checkAndSetRealNumber!
function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::Symbol, summand1::Symbol, summand2::Symbol)
if haskey(dictionary,sum) && dictionary[sum]!=nothing
if typeof(dictionary[sum]) <: Real && dictionary[sum] >= 0.0
difference = abs(dictionary[sum] - (dictionary[summand1]+dictionary[summand2]))
if difference > 1/(10^approxLevel)
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".")
end
else
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.")
end
else
merge!(dictionary, Dict(sum => dictionary[summand1]+dictionary[summand2]))
println("INFO at checking the input for the ",dictionaryType,": The key ",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." )
end
return dictionary
end #function checkAndSetSum!
function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String, validValues::Vector{String})
# TODO change checkAndAddString! to checkAndAddSymbol! ?
if haskey(dictionary,key) && dictionary[key]!=nothing
value = dictionary[key]
if typeof(value) == String
for validValue in validValues
if value == validValue
return dictionary
end
end
end
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues)
else
println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. It has to be one of the following String values: ", validValues,". For this calculation the default value '",defaultValue,"' will be used.")
merge!(dictionary, Dict(key => defaultValue))
end
return dictionary
end #function checkAndSetString!
# second method of function checkAndSetString! without validValues
function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String)
if haskey(dictionary,key) && dictionary[key]!=nothing
value = dictionary[key]
if typeof(value) == String
return dictionary
end
error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.")
else
println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. For this calculation the default value '",defaultValue,"' will be used.")
merge!(dictionary, Dict(key => defaultValue))
end
return dictionary
end #function checkAndSetString!
function checkAndSetSpeedLimit!(train::Dict)
v_limit_temp = 0.0
v_limit_kmh_temp = 0.0
if haskey(train, :v_limit) && train[:v_limit]!=nothing
if typeof(train[:v_limit]) <: Real && train[:v_limit] >= 0.0
v_limit_temp = train[:v_limit]
else
error("ERROR at checking the input for the train: The value of v_limit is no real floating point number >=0.0.")
end
end
if haskey(train, :v_limit_kmh) && train[:v_limit_kmh]!=nothing
if typeof(train[:v_limit_kmh]) <: Real && train[:v_limit_kmh] >= 0.0
v_limit_kmh_temp = train[:v_limit_kmh]
else
error("ERROR at checking the input for the train: The value of v_limit_kmh is no real floating point number >=0.0.")
end
else
delete!(train, :v_limit_kmh)
end
if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0
difference = abs(v_limit_temp - v_limit_kmh_temp/3.6)
if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ?
delete!(train, :v_limit_kmh)
println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." )
end
elseif v_limit_temp > 0.0
# do nothing
elseif v_limit_kmh_temp > 0.0
merge!(train, Dict(:v_limit => v_limit_kmh_temp/3.6))
else
# set a default value
merge!(train, Dict(:v_limit, 1000.0/3.6)) # set speed limit to 1000 km/h
println("INFO at checking the input for the train: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",train[:v_limit]," m/s is used." )
end
return train
end #function checkAndSetSpeedLimit!
function checkAndSetBrakingAcceleration!(train::Dict)
if haskey(train, :a_braking) && train[:a_braking]!=nothing
if typeof(train[:a_braking]) <: Real
if train[:a_braking] > 0.0
train[:a_braking] =-train[:a_braking]
println("INFO at checking the input for the train: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking=",train[:a_braking]," m/s^2 is used." )
elseif train[:a_braking] == 0.0
error("ERROR at checking the input for the train: The value for a_braking is 0.0. The braking acceleration has to be <0.0.")
end
else
error("ERROR at checking the input for the train: The value for a_braking is no real floating point number <0.0.")
end
else
# set a default value depending on the train type
if train[:type] == "freight"
a_braking = -0.225
elseif train[:type] == "passenger"
a_braking = -0.375
#elseif train[:type] == "passengerSuburban"
# a_braking = -0.525
# TODO: add suburban trains to train type?
end
merge!(train, Dict(:a_braking => a_braking))
println("INFO at checking the input for the train: The key for a_braking is missing. Because of the train type ",train[:type]," a_braking=",a_braking," m/s^2 will be assumed and used." )
end
return train
end #function checkAndSetBrakingAcceleration!
function checkAndSetRotationMassFactors!(train::Dict)
checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_train, :rotationMassFactor_train, "")
checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_t, :rotationMassFactor_t, "")
checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_w, :rotationMassFactor_w, "")
if haskey(train, :ξ_train) && train[:ξ_train]!=nothing
if train[:ξ_train]>0.0
if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing))
# TODO: is && train[:ξ_t]>0.0 necessary here?
difference = abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train])
if difference > 1/(10^approxLevel)
error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".")
end
end
else
error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.")
end
else
checkAndSetPositiveNumber!(train, "train", :ξ_t, "", 1.09)
if train[:m_w]>0.0
default_ξ_w = 1.06
else
default_ξ_w = 0.0
end
checkAndSetPositiveNumber!(train, "train", :ξ_w, "", default_ξ_w)
ξ_train=(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit)
if ξ_train <= 0.0
error("ERROR at checking the input for the train: The train's rotations mass factor has to be higher than 0.0 kg.")
end
merge!(train, Dict(:ξ_train => ξ_train))
end
return train
end #function checkAndSetRotationMassFactors!
function checkAndSetTractiveEffortVelocityPairs!(train::Dict) # pairs of velocity and tractive effort
if haskey(train,:tractiveEffortVelocityPairs) && train[:tractiveEffortVelocityPairs]!=nothing
pairs = train[:tractiveEffortVelocityPairs]
velocityMultiplier = 1.0
if (haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing) && (haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing)
println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs, F_T_pairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." )
elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing
println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs. The values for tractiveEffortVelocityPairs are used." )
elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing
println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." )
end
elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing
pairs = train[:F_T_pairs]
velocityMultiplier = 1.0
if haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing
println("WARNING at checking the input for the train: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." )
end
elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing
velocityMultiplier = 1000/3600
pairs=[]
for row in 1:length(train[:F_T_pairs_kmh])
push!(pairs, [train[:F_T_pairs_kmh][row][1]*velocityMultiplier, train[:F_T_pairs_kmh][row][2]])
end # for
else
error("ERROR at checking the input for the train: There has to be the key tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.")
end # if
# check if the elements of the array have the correct type
errorDetected=false
for row in 1:length(pairs)
if typeof(pairs[row][1]) <: Real && pairs[row][1]>=0.0
else
errorDetected=true
println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.")
end
if typeof(pairs[row][2]) <: Real && pairs[row][2]>=0.0
else
errorDetected=true
println("ERROR at checking the input for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.")
end
if row>=2 && pairs[row][1] <= pairs[row-1][1]
errorDetected=true
println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," (v=",pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",pairs[row-1][1]," m/s).")
end
end # for
if errorDetected
error("ERROR at checking the input for the train: Only real floating point number >=0.0 are allowed for speed and tractive effort. The speed values have to be listed from low to high.")
end
# create tractiveEffortVelocityPairs
if pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used
newPairs=[]
push!(newPairs, [0.0, pairs[1][2]])
println("INFO at checking the input for the train: The tractive effort for v=0.0 m/s is missing. Therefore the first given value F_T(v=",pairs[1][1]," m/s)=",pairs[1][2]," N will be used." )
for row in 1:length(pairs)
push!(newPairs, [pairs[row][1], pairs[row][2]])
end # for
merge!(train, Dict(:tractiveEffortVelocityPairs => newPairs))
else
merge!(train, Dict(:tractiveEffortVelocityPairs => pairs))
end
if length(pairs[1])>2
println("INFO according the train dictionary: Only the first two columns of train[:tractiveEffortVelocityPairs] are used in this tool.")
end
return train
end #function checkAndSetTractiveEffortVelocityPairs!
function getDefault_Δv_w(type::String) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s)
if type == "passenger"
# TODO if different passenger or freight trains are posiible, use: if startswith(type, "passenger"). exanples: passengerLocomotivehauled and passengerMotorCoachTrain
Δv_w=15.0/3.6
elseif type == "freight"
Δv_w=0.0
end # if
return Δv_w
end #function getDefault_Δv_w!
# function checkAndSetSections!(path::Path)
# # check the section information
# if haskey(path,:sections) && path.sections!=nothing
# # TODO: check typeof(path.sections) == Dict
# if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing)
# println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." )
# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
# println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." )
# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." )
# end
# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
# # TODO: check typeof(path.sections) == Array
# createSections!(path, :sectionStarts)
# if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." )
# end
# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# # TODO: check typeof(path.sections) == Array
# createSections!(path, :sectionStarts_kmh)
# else
# error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.")
# section = Dict(:s_start => 0.0,
# :s_end => 15.0,
# :v_limit => 1000.0/3.6,
# :f_Rp => 0.0)
# merge!(path, Dict(:sections => [section]))
# return path
# end
# sections = path.sections
# checkedSections = []
# increasing = false
# decreasing = false
# #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections?
# # check values for section==1
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_start, "m", 0.0) # first point of the section (in m)
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_end, "m", 0.0) # first point of the next section (in m)
# checkAndSetPositiveNumber!(sections[1], "path.sections[1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰)
# push!(checkedSections, sections[1])
# if sections[1][:s_start] < sections[1][:s_end]
# increasing = true
# elseif sections[1][:s_start] > sections[1][:s_end]
# decreasing = true
# else
# pop!(checkedSections)
# println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
# end
# for sectionNr in 2:length(sections)
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m)
# # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values
# #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real
# # defaultEnd = sections[sectionNr+1][:s_start]
# #end
# defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed!
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m)
# checkAndSetPositiveNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰)
# push!(checkedSections, sections[sectionNr])
# # compare the section's start and end position
# if sections[sectionNr][:s_start] < sections[sectionNr][:s_end]
# increasing = true
# elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end]
# decreasing = true
# else
# pop!(checkedSections)
# println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
# end
# if increasing && decreasing
# error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.")
# end
# if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end]
# error("ERROR at checking the input for the path.sections: The starting position of the ",section,". section (s=",sections[sectionNr][: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
# return path
# end #function checkAndSetSections!
# function createSections!(path::Path, key::Symbol)
# # read the section starting positions and corresponding information
# if key == :sectionStarts
# sectionStartsArray = path[:sectionStarts]
# conversionFactor = 1.0 # conversion factor between the units m/s and m/s
# if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." )
# end
# elseif key == :sectionStarts_kmh
# sectionStartsArray = path[:sectionStarts_kmh]
# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s
# elseif key == :characteristic_sections
# sectionStartsArray = path[:characteristic_sections]
# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s
# else
# error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.")
# end # if
# # check if the array is correct and if elements of the array have the correct type and valid values
# errorDetected = false
# if length(sectionStartsArray)<2
# error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].")
# end
# for row in 1:length(sectionStartsArray)
# if length(sectionStartsArray[row])>=3
# if length(sectionStartsArray[row])>3
# println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.")
# end
# else
# error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].")
# end
# if !(typeof(sectionStartsArray[row][1]) <: Real)
# errorDetected=true
# println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.")
# end
# if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0)
# errorDetected=true
# println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.")
# end
# if !(typeof(sectionStartsArray[row][3]) <: Real)
# errorDetected=true
# println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.")
# end
# end # for
# if errorDetected
# error("ERROR at checking the input for the path: The values of ",key," have to be corrected.")
# end
# sections = []
# for row in 2:length(sectionStartsArray)
# s_start = sectionStartsArray[row-1][1] # first point of the section (in m)
# s_end = sectionStartsArray[row][1] # first point of the next section (in m)
# v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s)
# f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰)
# section = Dict(:s_start => s_start,
# :s_end => s_end,
# :v_limit => v_limit,
# :f_Rp => f_Rp)
# push!(sections, section)
# end # for
# # s_start in first entry defines the path's beginning
# # s_end in last entry defines the path's ending
# merge!(path, Dict(:sections => sections))
# return path
# end #function createSections!
# function checkAndSetPOIs!(path::Path)
# # read the section starting positions and corresponding information
# if haskey(path, :pointsOfInterest)
# # if path.poi != nothing
# pointsOfInterest = path[:points_of_interest]
# sortingNeeded = false
# errorDetected = false
# for element in 1:length(pointsOfInterest)
# if typeof(pointsOfInterest[element]) <: Real
# if element > 1
# if pointsOfInterest[element] < pointsOfInterest[element-1]
# sortingNeeded = true
# println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.")
# end
# end
# else
# errorDetected = true
# println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.")
# end
# end # for
# if errorDetected
# error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.")
# end
# if sortingNeeded == true
# sort!(pointsOfInterest)
# end
# copiedPOIs = []
# for element in 1:length(pointsOfInterest)
# if element == 1
# push!(copiedPOIs, pointsOfInterest[element])
# elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1]
# push!(copiedPOIs, pointsOfInterest[element])
# end
# end # for
# path[:points_of_interest ] = copiedPOIs
# # else
# # println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.")
# # delete!(path, :points_of_interest)
# # end
# end
# return path
# end #function checkAndSetPOIs!
#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool
function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool
unusedKeys = []
# find unused keys in allKeys
for key in allKeys
used = false
for usedKey in usedKeys
if key == usedKey
used = true
break
end
end
if !used
push!(unusedKeys, key)
end
end
if length(unusedKeys)>0
println("INFO at checking the input for the ",dictionaryType,": The following Keywords are not used in this tool:")
for key in unusedKeys
println(" - ",key)
end
end
end #function informAboutUnusedKeys

View File

@ -14,9 +14,9 @@ settings = Dict()
@testset "load data" begin @testset "load data" begin
println("testing load train data") println("testing load train data")
push!(trains, :freight => @time TrainRuns.importFromYaml(:train, "test/data/trains/freight.yaml")) push!(trains, :freight => @time Train("test/data/trains/freight.yaml"))
push!(trains, :local => @time TrainRuns.importFromYaml(:train, "test/data/trains/local.yaml")) push!(trains, :local => @time Train("test/data/trains/local.yaml"))
push!(trains, :longdistance => @time TrainRuns.importFromYaml(:train, "test/data/trains/longdistance.yaml")) push!(trains, :longdistance => @time Train("test/data/trains/longdistance.yaml"))
println("testing load path data") println("testing load path data")
push!(paths, :const => @time Path("test/data/paths/const.yaml")) push!(paths, :const => @time Path("test/data/paths/const.yaml"))
@ -79,7 +79,7 @@ anticipated = Dict(
@time result = trainrun(test[1][2],test[2][2]) @time result = trainrun(test[1][2],test[2][2])
expected = anticipated[:default][Symbol(test_name)] expected = anticipated[:default][Symbol(test_name)]
# compare result to test data set # compare result to test data set
@test isapprox(result, expected, atol=0.01) @test isapprox(result, expected, rtol=0.1)
println("--------------------") println("--------------------")
end end