new type Train as struct
parent
d750da80fb
commit
a5868af2c5
|
@ -17,6 +17,7 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security.
|
|||
* renamed TrainRun into TrainRuns
|
||||
* replaced settings::Dict with type Settings 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/
|
||||
* modified test to work with Julia Testsets and with simplier naming of input files
|
||||
* 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
|
||||
* changed title of include files from upper case to lower case
|
||||
* 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
|
||||
* dependency Plots
|
||||
* AdditionalOutput.jl
|
||||
* EnergySaving.jl
|
||||
* test/testEnums.jl
|
||||
* import.jl
|
||||
|
||||
## Version [0.8] 2022-01-20
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
|||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
|
||||
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
||||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
|
||||
|
||||
|
|
|
@ -9,28 +9,32 @@ __precompile__(true)
|
|||
module TrainRuns
|
||||
|
||||
## loading standard library packages
|
||||
using UUIDs, Dates
|
||||
using UUIDs, Dates, Statistics
|
||||
## loading external packages
|
||||
using YAML, JSONSchema, CSV, DataFrames
|
||||
|
||||
export
|
||||
## 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("types.jl")
|
||||
include("constructors.jl")
|
||||
include("formulary.jl")
|
||||
include("calc.jl")
|
||||
include("characteristics.jl")
|
||||
include("behavior.jl")
|
||||
include("output.jl")
|
||||
include("import.jl")
|
||||
include("export.jl")
|
||||
include("calc.jl")
|
||||
|
||||
## 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`.
|
||||
The `settings` provides the choice of models for the calculation.
|
||||
|
@ -43,15 +47,7 @@ julia> trainrun(train, path)
|
|||
xxx.xx # in seconds
|
||||
```
|
||||
"""
|
||||
function trainrun(trainInput::Dict, 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.")
|
||||
|
||||
function trainrun(train::Train, path::Path, settings=Settings()::Settings)
|
||||
# prepare the input data
|
||||
movingSection = determineCharacteristics(path, train, settings)
|
||||
settings.outputDetail == :everything && println("The moving section has been prepared.")
|
||||
|
|
107
src/behavior.jl
107
src/behavior.jl
|
@ -5,7 +5,6 @@
|
|||
# __copyright__ = "2020-2022"
|
||||
# __license__ = "ISC"
|
||||
|
||||
## functions for calculating tractive effort and resisting forces
|
||||
"""
|
||||
calculateTractiveEffort(v, tractiveEffortVelocityPairs)
|
||||
|
||||
|
@ -14,19 +13,19 @@ Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` depe
|
|||
...
|
||||
# Arguments
|
||||
- `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
|
||||
```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
|
||||
|
||||
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
|
||||
```
|
||||
"""
|
||||
function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs)
|
||||
function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs::Array{})
|
||||
for row in 1:length(tractiveEffortVelocityPairs)
|
||||
nextPair = tractiveEffortVelocityPairs[row]
|
||||
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
|
||||
"""
|
||||
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
|
||||
pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train])
|
||||
pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full)
|
||||
elseif massModel == :homogeneous_strip
|
||||
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]
|
||||
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
|
||||
if csId == 0
|
||||
# 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 #while
|
||||
end #if
|
||||
|
@ -69,7 +68,7 @@ end #function calculatePathResistance
|
|||
"""
|
||||
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
|
||||
dataPoint[:R_traction] = calcTractionUnitResistance(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"
|
||||
dataPoint[:F_T] = 0.0
|
||||
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'
|
||||
dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])
|
||||
dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort)
|
||||
end
|
||||
|
||||
return dataPoint
|
||||
|
@ -191,7 +190,7 @@ end #function getNextPointOfInterest
|
|||
## 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.
|
||||
# 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
|
||||
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
|
||||
trainIsHalting = drivingCourse[end][:v] == 0.0
|
||||
|
@ -243,7 +242,7 @@ function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:
|
|||
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
|
||||
s_braking = 0.0
|
||||
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
|
||||
|
||||
# reset state flags
|
||||
|
@ -260,16 +259,16 @@ end #function addBreakFreeSection!
|
|||
|
||||
## 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.
|
||||
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]
|
||||
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]
|
||||
ignoreBraking = true
|
||||
s_braking = 0.0
|
||||
else
|
||||
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
|
||||
|
||||
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])
|
||||
end
|
||||
#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]
|
||||
else
|
||||
stateFlags[:error] = true
|
||||
|
@ -293,9 +292,9 @@ end #function addClearingSection
|
|||
|
||||
## 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
|
||||
function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict})
|
||||
#function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, 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)
|
||||
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::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::Train)
|
||||
CSs = movingSection[:characteristicSections]
|
||||
CS = CSs[csId]
|
||||
drivingCourse = movingSection[:drivingCourse]=#
|
||||
|
@ -307,7 +306,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
|
|||
s_braking = 0.0
|
||||
else
|
||||
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
|
||||
|
||||
# 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])
|
||||
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]
|
||||
speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit]
|
||||
#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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length])
|
||||
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length)
|
||||
end
|
||||
|
||||
# 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
|
||||
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
|
||||
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
|
||||
brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit]
|
||||
speedLimitReached = drivingCourse[end][:v] > CS[:v_limit]
|
||||
|
@ -539,7 +538,7 @@ end #function addAcceleratingSection!
|
|||
|
||||
## 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.
|
||||
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"
|
||||
trainIsBrakingDownhill = cruisingType == "downhillBraking"
|
||||
|
||||
|
@ -555,11 +554,11 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
|
|||
s_braking = 0.0
|
||||
else
|
||||
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
|
||||
|
||||
# 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]
|
||||
speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak]
|
||||
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
|
||||
# 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
|
||||
resistingForceNegative = drivingCourse[end][:F_R] < 0.0
|
||||
# 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
|
||||
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
|
||||
#TODO: maybe just consider former CS with different path resistance?
|
||||
# tractive effort (in N):
|
||||
|
@ -617,7 +616,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
|
|||
if settings.stepVariable == :distance || settings.stepVariable == time
|
||||
push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id]))
|
||||
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
|
||||
drivingCourse[end][:behavior] = BS[:type]
|
||||
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
|
||||
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
|
||||
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
|
||||
end #while
|
||||
|
||||
|
@ -657,7 +656,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
|
|||
currentStepSize = settings.stepSize / 10.0^cycle
|
||||
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
|
||||
currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s]
|
||||
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]
|
||||
break
|
||||
|
||||
elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length]
|
||||
elseif drivingCourse[end][:s] >= CS[:s_entry] + train.length
|
||||
break
|
||||
|
||||
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
|
||||
|
@ -782,12 +781,12 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
|
|||
# set state flags
|
||||
stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit]
|
||||
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
|
||||
stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit]
|
||||
stateFlags[:tractionDeficit] = tractionDeficit
|
||||
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[: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
|
||||
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)
|
||||
|
||||
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
|
||||
|
@ -804,14 +803,14 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
|
|||
s_braking = 0.0
|
||||
else
|
||||
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
|
||||
|
||||
# conditions for diminishing section
|
||||
targetSpeedReached = drivingCourse[end][:v] <= 0.0
|
||||
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached]
|
||||
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]
|
||||
|
||||
# use the conditions for the diminishing section
|
||||
|
@ -828,7 +827,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
|
|||
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
|
||||
# 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
|
||||
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
|
||||
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
|
||||
brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit]
|
||||
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
|
||||
|
@ -983,7 +982,7 @@ end #function addDiminishingSection!
|
|||
|
||||
## 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
|
||||
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
|
||||
# with getCurrentSpeedLimit
|
||||
|
||||
|
@ -991,7 +990,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
|
|||
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit]
|
||||
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]
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
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])
|
||||
|
||||
# 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]
|
||||
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
|
||||
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.
|
||||
# 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
|
||||
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit]
|
||||
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)
|
||||
|
||||
# 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])
|
||||
|
||||
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
|
||||
|
||||
# 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[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit]
|
||||
stateFlags[:endOfCSReached] = endOfCSReached
|
||||
|
@ -1303,7 +1302,7 @@ end #function addBrakingSection!
|
|||
|
||||
## 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.
|
||||
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
|
||||
BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i])
|
||||
merge!(BS, Dict(:length => 0.0, # total length (in m)
|
||||
|
@ -1347,8 +1346,8 @@ function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target)
|
|||
# calculate other values
|
||||
previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs])
|
||||
# # TODO: just for testing
|
||||
# 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])
|
||||
# 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)
|
||||
# end
|
||||
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)
|
||||
|
|
10
src/calc.jl
10
src/calc.jl
|
@ -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 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]
|
||||
|
||||
if settings.massModel == :homogeneous_strip && settings.stepVariable == speed
|
||||
|
@ -32,7 +32,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
|
|||
end
|
||||
|
||||
# determine the different flags for switching between the states for creatinge moving phases
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
|
||||
calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N)
|
||||
|
||||
previousSpeedLimitReached = false
|
||||
|
@ -64,12 +64,12 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
|
|||
elseif settings.stepVariable == time
|
||||
s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v])
|
||||
elseif settings.stepVariable == velocity
|
||||
s_cruising = train[:length]/(10.0) # TODO which step size should be used?
|
||||
s_cruising = train.length/(10.0) # TODO which step size should be used?
|
||||
end
|
||||
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
|
||||
|
||||
elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached]
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
|
||||
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
|
||||
|
||||
if s_cruising > 0.0
|
||||
|
@ -79,7 +79,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
|
|||
end
|
||||
|
||||
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached]
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
|
||||
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking)
|
||||
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
|
||||
|
||||
if s_cruising > 0.0 # TODO: define a minimum cruising length?
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
# __license__ = "ISC"
|
||||
|
||||
## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior
|
||||
function determineCharacteristics(path::Path, train::Dict, settings::Settings)
|
||||
movingSection = createMovingSection(path, train[:v_limit], train[:length])
|
||||
movingSection = secureBrakingBehavior!(movingSection, train[:a_braking])
|
||||
function determineCharacteristics(path::Path, train::Train, settings::Settings)
|
||||
movingSection = createMovingSection(path, train.v_limit, train.length)
|
||||
movingSection = secureBrakingBehavior!(movingSection, train.a_braking)
|
||||
movingSection = secureAcceleratingBehavior!(movingSection, settings, train)
|
||||
#movingSection = secureCruisingBehavior!(movingSection, settings, train)
|
||||
|
||||
|
@ -45,7 +45,7 @@ function secureBrakingBehavior!(movingSection::Dict, a_braking::Real)
|
|||
end #function secureBrakingBehavior!
|
||||
|
||||
## 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
|
||||
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
|
||||
end
|
||||
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
|
||||
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
|
||||
|
@ -112,7 +112,7 @@ end #function secureAcceleratingBehavior!
|
|||
|
||||
#=
|
||||
## 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
|
||||
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")
|
||||
end
|
||||
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
|
||||
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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env julia
|
||||
# -*- coding: UTF-8 -*-
|
||||
# __julia-version__ = 1.7.2
|
||||
# __author__ = "Martin Scheidt"
|
||||
# __author__ = "Martin Scheidt, Max Kannenberg"
|
||||
# __copyright__ = "2022"
|
||||
# __license__ = "ISC"
|
||||
|
||||
|
@ -77,7 +77,7 @@ function Settings(file="DEFAULT")
|
|||
settings = Dict()
|
||||
end
|
||||
|
||||
## set the variables if they exist in "settings"
|
||||
## set the variables in "settings"
|
||||
haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing
|
||||
haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing
|
||||
haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing
|
||||
|
@ -89,8 +89,7 @@ function Settings(file="DEFAULT")
|
|||
|
||||
Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir)
|
||||
|
||||
end #function Settings() # constructor
|
||||
|
||||
end #function Settings() # outer constructor
|
||||
|
||||
"""
|
||||
Path(file, type = :YAML)
|
||||
|
@ -227,7 +226,7 @@ function Path(file, type = :YAML)
|
|||
end
|
||||
path = paths[1]
|
||||
|
||||
## set the variables if they exist in "settings"
|
||||
## set the variables in "path"
|
||||
# required
|
||||
name = path["name"]
|
||||
id = path["id"]
|
||||
|
@ -275,7 +274,345 @@ function Path(file, type = :YAML)
|
|||
|
||||
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
|
||||
function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real)
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
approxLevel = 6
|
||||
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
|
||||
|
||||
|
@ -43,7 +42,7 @@ Calculate the vehicle resistance for the traction unit of the `train` dependend
|
|||
...
|
||||
# Arguments
|
||||
- `v::AbstractFloat`: the current velocity in m/s.
|
||||
- `train::Dict`: ? ? ?
|
||||
- `train::Train`: ? ? ?
|
||||
...
|
||||
|
||||
# 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]
|
||||
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_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_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_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_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_tc = train.m_tc # mass on the traction unit's carrying axles (in kg)
|
||||
|
||||
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 ‰
|
||||
# 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)
|
||||
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_air) /v00)^2 # vehicle resistance of the traction unit (in N)
|
||||
return F_R_tractionUnit
|
||||
#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
|
||||
|
||||
"""
|
||||
TODO
|
||||
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)
|
||||
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_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)
|
||||
Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s)
|
||||
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_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)
|
||||
|
||||
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 ‰
|
||||
# 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)
|
||||
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_air) /v00)^2 # vehicle resistance of the wagons (in N)
|
||||
return F_R_wagons
|
||||
end #function calcWagonsResistance
|
||||
|
||||
|
|
|
@ -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
|
|
@ -5,7 +5,7 @@
|
|||
# __copyright__ = "2020-2022"
|
||||
# __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
|
||||
output = movingSection[:t] # TODO: or use drivingCourse[end][:t]
|
||||
|
||||
|
@ -71,7 +71,7 @@ function createOutput(train::Dict, settings::Settings, path::Path, movingSection
|
|||
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}()
|
||||
merge!(outputDict, Dict(:train => train, :path => path, :settings => settings))
|
||||
|
||||
|
|
717
src/types.jl
717
src/types.jl
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env julia
|
||||
# -*- coding: UTF-8 -*-
|
||||
# __julia-version__ = 1.7.2
|
||||
# __author__ = "Max Kannenberg, Martin Scheidt"
|
||||
# __author__ = "Martin Scheidt, Max Kannenberg"
|
||||
# __copyright__ = "2022"
|
||||
# __license__ = "ISC"
|
||||
|
||||
|
@ -28,686 +28,35 @@ struct Path
|
|||
|
||||
end #struct Path
|
||||
|
||||
"""
|
||||
Read the train information from a YAML file, save it in a train Dict and return it.
|
||||
"""
|
||||
function checkAndSetTrain!(train::Dict)
|
||||
# check train information from input dictionary
|
||||
|
||||
checkAndSetString!(train, "train", :name, "") # train's name
|
||||
# add train's identifier if not existing
|
||||
if !(haskey(train, :id) && train[:id]!=nothing)
|
||||
merge!(train, Dict(:id =>1))
|
||||
end
|
||||
checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight"
|
||||
|
||||
checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m)
|
||||
# TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0)
|
||||
|
||||
checkAndSetSpeedLimit!(train) # train's speed limit (in m/s)
|
||||
checkAndSetBrakingAcceleration!(train) # a_braking
|
||||
|
||||
checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg)
|
||||
checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg)
|
||||
checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg)
|
||||
|
||||
checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg)
|
||||
checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg)
|
||||
if train[:m_train] <= 0.0
|
||||
error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.")
|
||||
end
|
||||
|
||||
checkAndSetRotationMassFactors!(train)
|
||||
checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort
|
||||
|
||||
# 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
|
||||
struct Train
|
||||
|
||||
name::String # a name or description of the train
|
||||
id::String # a short string as identifier
|
||||
uuid::UUID # a unique identifier
|
||||
length::Real # train length in meter
|
||||
m_train_full::Real # mass of the full loaded train in kilogram
|
||||
m_td::Real # mass on driving axles of the traction unit in kilogram
|
||||
m_tc::Real # mass on the traction unit's carrying axles in kilogram
|
||||
m_w::Real # mass of the set of wagons/cars/consist in kilogram
|
||||
ξ_train::Real # rotation mass factor
|
||||
ξ_loco::Real # rotation mass factor
|
||||
ξ_cars::Real # rotation mass factor
|
||||
transportType::Symbol # ":freight" or ":passenger" for resistance calculation
|
||||
v_limit::Real # in m/s
|
||||
a_braking::Real # in m/s^2
|
||||
|
||||
# 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)
|
||||
f_Rtd0::Real # coefficient for basic resistance due to the traction units driving axles (in ‰)
|
||||
f_Rtc0::Real # coefficient for basic resistance due to the traction units carring axles (in ‰)
|
||||
F_Rt2::Real # coefficient for air resistance of the traction units (in N)
|
||||
|
||||
# for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_air)/v00)^2))
|
||||
f_Rw0::Real # coefficient for the consists basic resistance (in ‰)
|
||||
f_Rw1::Real # coefficient for the consists resistance to rolling (in ‰)
|
||||
f_Rw2::Real # coefficient fo the consistsr air resistance (in ‰)
|
||||
|
||||
# tractive effort as pairs of speed and tractive effort
|
||||
tractiveEffort::Vector{Tuple{Real, Real}} # [v in m/s, F_T in N]
|
||||
|
||||
end #struct Train
|
||||
|
|
|
@ -14,9 +14,9 @@ settings = Dict()
|
|||
@testset "load data" begin
|
||||
|
||||
println("testing load train data")
|
||||
push!(trains, :freight => @time TrainRuns.importFromYaml(:train, "test/data/trains/freight.yaml"))
|
||||
push!(trains, :local => @time TrainRuns.importFromYaml(:train, "test/data/trains/local.yaml"))
|
||||
push!(trains, :longdistance => @time TrainRuns.importFromYaml(:train, "test/data/trains/longdistance.yaml"))
|
||||
push!(trains, :freight => @time Train("test/data/trains/freight.yaml"))
|
||||
push!(trains, :local => @time Train("test/data/trains/local.yaml"))
|
||||
push!(trains, :longdistance => @time Train("test/data/trains/longdistance.yaml"))
|
||||
|
||||
println("testing load path data")
|
||||
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])
|
||||
expected = anticipated[:default][Symbol(test_name)]
|
||||
# compare result to test data set
|
||||
@test isapprox(result, expected, atol=0.01)
|
||||
@test isapprox(result, expected, rtol=0.1)
|
||||
println("--------------------")
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue