From 9343e1cc74d9452c497762cdb034f22dc5691799 Mon Sep 17 00:00:00 2001 From: Max Kannenberg <95709892+MaxKannenberg@users.noreply.github.com> Date: Fri, 8 Apr 2022 17:30:34 +0200 Subject: [PATCH] Refactor input and import and set default values --- .../settings_distanceStep_massPoint.yaml | 4 +- .../train_freight_V90withOreConsist.yaml | 4 +- data/trains/train_passenger_IC2.yaml | 4 +- .../train_passenger_SiemensDesiroClassic.yaml | 4 +- src/EnergySaving.jl | 2 +- src/Import.jl | 586 +------------ src/Input.jl | 784 ++++++++++++------ src/TrainRun.jl | 9 +- 8 files changed, 570 insertions(+), 827 deletions(-) diff --git a/data/settings/settings_distanceStep_massPoint.yaml b/data/settings/settings_distanceStep_massPoint.yaml index 8096372..fc616ed 100644 --- a/data/settings/settings_distanceStep_massPoint.yaml +++ b/data/settings/settings_distanceStep_massPoint.yaml @@ -7,6 +7,6 @@ settings: stepSize: 10 # step size (unit depends on stepVariable s in m, t in s and v in m/s) operationModeMinimumRunningTime: true # operation mode "minimum running time" operationModeMinimumEnergyConsumption: false # operation mode "minimum energy consumption" - typeOfOutput: "CSV" # output as "julia dictionary" or as "CSV" - detailOfOutput: "everything" # should the output be only the value of the "running time" or an array of "points of interest" or the complete "driving course" as array or a dictionary with "everything"? + typeOfOutput: "julia dictionary" # output as "julia dictionary" or as "CSV" + detailOfOutput: "driving course" # should the output be only the value of the "running time" or an array of "points of interest" or the complete "driving course" as array or a dictionary with "everything"? csvDirectory: "~/Desktop/TrainRun" diff --git a/data/trains/train_freight_V90withOreConsist.yaml b/data/trains/train_freight_V90withOreConsist.yaml index fd2ec5e..59c82bb 100644 --- a/data/trains/train_freight_V90withOreConsist.yaml +++ b/data/trains/train_freight_V90withOreConsist.yaml @@ -2,7 +2,7 @@ --- train: name: "V 90 with 10 ore wagons of type Facs 124" # (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 and https://dybas.de/dybas/gw/gw_f_1/g124.html) - l_train: 205.3 # in m (source: FBS: DB 290 with 10x Facs124) + length: 205.3 # in m (source: FBS: DB 290 with 10x Facs124) m_td: 80000 # mass on driving axles of the traction unit in kg (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) m_tc: 0 # mass on carrying axles of the traction unit in kg (no carrying axles; source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) m_w: 850000 # mass of the consist (set of wagons) in kg (source: FBS: 10x Facs124) @@ -10,7 +10,7 @@ train: rotationMassFactor_t: 1.09 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit) rotationMassFactor_w: 1.03 # (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Güterwagenzug beladen" -> 1.03 to 1.04) powerType: diesel # diesel or electric (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - trainType: freight # "freight" or "passenger" or "motorCoachTrain" (source: https://dybas.de/dybas/gw/gw_f_1/g124.html) + type: freight # "freight" or "passenger" (source: https://dybas.de/dybas/gw/gw_f_1/g124.html) v_limit: # in m/s v_limit_kmh: 80 # in km/h (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) a_braking: -0.4124 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 290 with with 10x Facs124) diff --git a/data/trains/train_passenger_IC2.yaml b/data/trains/train_passenger_IC2.yaml index f1d5f33..89ba344 100644 --- a/data/trains/train_passenger_IC2.yaml +++ b/data/trains/train_passenger_IC2.yaml @@ -2,7 +2,7 @@ --- train: name: "Intercity 2 (Traxx P160 AC2 + double deck coaches)" # (source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario#Intercity_2 and https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) - l_train: 152 # in m (source: FBS: DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) + length: 152 # in m (source: FBS: DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) m_td: 84000 # mass on driving axles of the traction unit in kg (source: FBS: DB146.5) m_tc: 0 # mass on carrying axles of the traction unit in kg (no carrying axles; source: FBS: DB146.5) m_w: 309000 # mass of the consist (set of wagons) in kg (source: FBS: 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) @@ -10,7 +10,7 @@ train: rotationMassFactor_t: 1.09 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit) rotationMassFactor_w: 1.06 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for freight wagons) powerType: electric # diesel or electric (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)#Gemeinsame_Merkmale) - trainType: passenger # "freight" or "passenger" or "motorCoachTrain" (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) + type: passenger # "freight" or "passenger" (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) v_limit: # in m/s v_limit_kmh: 160 # in km/h (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)#Gemeinsame_Merkmale) a_braking: -0.3507 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) diff --git a/data/trains/train_passenger_SiemensDesiroClassic.yaml b/data/trains/train_passenger_SiemensDesiroClassic.yaml index 76500b8..666a37e 100644 --- a/data/trains/train_passenger_SiemensDesiroClassic.yaml +++ b/data/trains/train_passenger_SiemensDesiroClassic.yaml @@ -2,7 +2,7 @@ --- train: name: "Siemens Desiro Classic" # (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - l_train: 41.7 # in m (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) + length: 41.7 # in m (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) m_td: 52800 # mass on driving axles of the traction unit in kg (source: FBS: DB 642; proportionately to the number of axles: 4 to 2, see: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) m_tc: 35200 # mass on carrying axles of the traction unit in kg (source: FBS: DB 642; proportionately to the number of axles: 4 to 2, see: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) m_w: 0 # mass of the consist (set of wagons) in kg (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) @@ -10,7 +10,7 @@ train: rotationMassFactor_t: rotationMassFactor_w: powerType: diesel # diesel or electric (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - trainType: motorCoachTrain # "freight" or "passenger" or "motorCoachTrain" (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) + type: passenger # "freight" or "passenger" (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) v_limit: # in m/s v_limit_kmh: 120 # in km/h (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) a_braking: -0.4253 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 642) diff --git a/src/EnergySaving.jl b/src/EnergySaving.jl index 9d696ab..26c4d18 100644 --- a/src/EnergySaving.jl +++ b/src/EnergySaving.jl @@ -14,7 +14,7 @@ # TODO from 2022/01/22: use always copyCharacteristicSection and don't do it manually like "csModified=Dict(:id => csOriginal[:id], ..." three times # TODO from 2022/03/18: stateFlags need to be added to functions that add behavior sections # TODO from 2022/03/21: consider previous speed limits during the coasting section in case F_R < 0.0 and the train is getting faster - +# TODO from 2002/04/07: the train type is only devided in passenger and freight and not motorCoachTrain anymore because this is only used for EnergySaving. If EnergySaving will be reactivated it the train type also has to change from enum to String or Symbol module EnergySaving # include modules of TrainRunCalc diff --git a/src/Import.jl b/src/Import.jl index 567318e..baeb5e8 100644 --- a/src/Import.jl +++ b/src/Import.jl @@ -9,584 +9,36 @@ module Import import YAML -export importYamlFiles, importYamlFile - -@enum trainType passenger=1 freight=2 motorCoachTrain=3 +export importYamlFiles, importFromYaml """ 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, settingsDirectory::String) - train = importTrainFromYaml(trainDirectory) - path = importPathFromYaml(pathDirectory) - settings = importSettingsFromYaml(settingsDirectory) + train = importFromYaml(:train, trainDirectory) + path = importFromYaml(:path, pathDirectory) + settings = importFromYaml(:settings, settingsDirectory) - return (train, path, settings) + return (train, path, settings) end #function importYamlFiles """ - Read the input information from one of the YAML files for train, path or settings, save it in a Dictionary and return it. + Read the train information from a YAML file, save it in a Dict and return it. """ -function importYamlFile(dataType::Symbol, directory::String) - if dataType == :train - return importTrainFromYaml(directory) - elseif dataType == :path - return importPathFromYaml(directory) - elseif dataType == :settings - return importSettingsFromYaml(directory) - else - error("Wrong dataType in function importYamlFile") +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 - end #function importYamlFile - -""" -Read the train information from a YAML file, save it in a train Dict and return it. -""" -function importTrainFromYaml(trainDirectory::String) - data = YAML.load(open(trainDirectory)) - - name = getString!(data, "train", "name") # train's name - id=1 # train's identifier - type = getTrainType!(data) # "passenger" or "freight" or "motorCoachTrain" - - trainLength = getPositiveNumber!(data, "train", "l_train", "m", true) # total length (in m) - v_limit = getSpeedLimit!(data) # train's speed limit (in m/s) - a_braking = get_a_braking!(data) # a_braking - - m_td = getPositiveNumber!(data, "train", "m_td", "kg", true) # mass on the traction unit's driving axles (in kg) - m_tc = getPositiveNumber!(data, "train", "m_tc", "kg", false) # mass on the traction unit's carrying axles (in kg) - m_t=m_td+m_tc # mass of the traction unit (in kg) - m_w = getPositiveNumber!(data, "train", "m_w", "kg", false) # mass of the set of wagons (consist) (in kg) - m_train=m_t+m_w # total mass (in kg) - - (ξ_train, ξ_t, ξ_w) = getRotationMassFactors!(data, m_train, m_t, m_w) - - tractiveEffortVelocityPairs = getTractiveEffortVelocityPairs!(data) # pairs of velocity and tractive effort - - # coefficients for the vehicle resistance of the traction unit - Δv_t=15.0/3.6 # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - f_Rtd0 = getPositiveNumber!(data, "train", "f_Rtd0", "‰", false) # coefficient for basic resistance due to the traction units driving axles (in ‰) - f_Rtc0 = getPositiveNumber!(data, "train", "f_Rtc0", "‰", false) # coefficient for basic resistance due to the traction units carring axles (in ‰) - F_Rt2 = getPositiveNumber!(data, "train", "F_Rt2", "N", false) # coefficient for air resistance of the traction units (in N) - - - - # coefficients for the vehicle resistance of the set of wagons (consist) - Δv_w = get_Δv_w!(type) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - f_Rw0 = getPositiveNumber!(data, "train", "f_Rw0", "‰", false) # coefficient for basic resistance of the set of wagons (consist) (in ‰) - f_Rw1 = getPositiveNumber!(data, "train", "f_Rw1", "‰", false) # coefficient for the consists resistance to rolling (in ‰) - f_Rw2 = getPositiveNumber!(data, "train", "f_Rw2", "‰", false) # coefficient fo the consistsr air resistance (in ‰) - - informAboutUnusedKeys(data, "train") # inform the user, which keywords of the imported data are not used in this tool - - # create the train Dictionary - train= Dict(:name => name, # train's name - :id => id, # train's identifier - :type => type, # type of train "passenger" or "freight" or "motorCoachTrain" - #= 01/05 old without enum :type => type, # type of train "passenger" or "freight" or "motor coach train" =# - :length => trainLength, # total length (in m) - :v_limit => v_limit, # trains speed limit (in m/s) - :a_braking => a_braking, # braking acceleration (in m/s^2) - :m_train => m_train, # total mass (in kg) - :ξ_train => ξ_train, # rotation mass factor of the whole train (without unit) - # if not available use ξ_t and ξ_w - - # traction unit - :m_t => m_t, # mass of the traction unit (in kg) - :m_td => m_td, # mass on the traction units driving axles (in kg) - :m_tc => m_tc, # mass on the traction units carrying axles (in kg) - :ξ_t => ξ_t, # rotation mass factor of the traction unit (without unit) - # in case ξ_train is not available - :tractiveEffortVelocityPairs => tractiveEffortVelocityPairs, # list of velocities and their corresponding tractive effort (in [m/s , N]) - - :f_Rtd0 => f_Rtd0, # coefficient for basic resistance due to the traction units driving axles (in ‰) - :f_Rtc0 => f_Rtc0, # coefficient for basic resistance due to the traction units carring axles (in ‰) - :F_Rt2 => F_Rt2, # coefficient for air resistance of the traction units (in N) - :Δv_t => Δv_t, # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - - # set of wagons - :m_w => m_w, # mass of the set of wagons (in kg) - :ξ_w => ξ_w, # rotation mass factor of the set of wagons (without unit) - # in case ξ_train is not available - :f_Rw0 => f_Rw0, # coefficient for basic resistance of the set of wagons (in ‰) - :f_Rw1 => f_Rw1, # coefficient for resistance to rolling of the set of wagons (in ‰) - :f_Rw2 => f_Rw2, # coefficient for air resistance of the set of wagons (in ‰) - :Δv_w => Δv_w) # coefficient for velocitiy difference between set of wagons and outdoor air (in m/s) - - return train -end #function importTrainFromYaml - -function importPathFromYaml(pathDirectory::String) - # read path information from a YAML file, save it in a path Dict and return it - data = YAML.load(open(pathDirectory)) - - name = getString!(data, "path", "name") - id=1 # path identifier - sections = getSections!(data) - - # save values in the path Dict - path = Dict(:name => name, - :id => id, - :sections => sections) - - addPointsOfInterest!(path, data) - - informAboutUnusedKeys(data, "path") # inform the user, which keywords of the imported data are not used in this tool - - return path -end # function importPathFromYaml - - -## settings for the calculation -function importSettingsFromYaml(settingsDirectory::String) - # read setting information from a YAML file, save it in a settings Dict and return it - data = YAML.load(open(settingsDirectory)) - - # initialize the settings Dictionary - settings = Dict(:massModel => "", # model type of the train's mass "mass point" or "homogeneous strip" - :stepVariable => "", # step variable of the step method "s in m", "t in s" or "v in m/s" - :stepSize => 0.0, # step size (unit depends on stepVariable s in m, t in s and v in m/s) - :operationModeMinimumRunningTime => false, # operation mode "minimum running time" - :operationModeMinimumEnergyConsumption => false, # operation mode "minimum energy consumption" - :typeOfOutput => "", # output as "julia dictionary" or as "CSV" - :csvDirectory => "", # path of the folder in which the CSV files willl be saved - :detailOfOutput => "") # detail of output "minimal" or "everything" - - settings[:massModel] = getString!(data, "settings", "massModel", ["mass point", "homogeneous strip"]) # model type of the train's mass "mass point" or "homogeneous strip" - settings[:stepVariable] = getString!(data, "settings", "stepVariable", ["s in m", "t in s", "v in m/s"]) # step variable of the step method "s in m", "t in s" or "v in m/s" - settings[:stepSize] = getPositiveNumber!(data, "settings", "stepSize", "("*settings[:stepVariable]*")", true) # step size (unit depends on stepVariable: s in m, t in s and v in m/s) - settings[:operationModeMinimumRunningTime] = getBool!(data, "settings", "operationModeMinimumRunningTime") # operation mode "minimum running time" - settings[:operationModeMinimumEnergyConsumption] = getBool!(data, "settings", "operationModeMinimumEnergyConsumption") # operation mode "minimum energy consumption" - settings[:typeOfOutput] = getString!(data, "settings", "typeOfOutput", ["julia dictionary", "CSV"]) # output as "julia dictionary" or as "CSV" - - if settings[:typeOfOutput] == "CSV" - settings[:csvDirectory] = getString!(data, "settings", "csvDirectory") - # TODO: it could be checked if the path is existing on the pc - end # if - - settings[:detailOfOutput] = getString!(data, "settings", "detailOfOutput", ["running time", "points of interest", "driving course", "everything"]) # should the output be only the value of the "running time", or an array of "points of interest" or the complete "driving course" as array or a dictionary with "everything"? - # 30/31 old: settings[:detailOfOutput] = getString!(data, "settings", "detailOfOutput", ["minimal", "points of interest", "driving course"]) # should the output be "minimal" or are "points of interest" or the complete "driving course" required? - - informAboutUnusedKeys(data, "settings") # inform the user, which keywords of the imported data are not used in this tool - - return settings -end # function importSettingsFromYaml - -function getBool!(data::Dict, dataSet::String, key::String) - if haskey(data[dataSet],key) && data[dataSet][key]!=nothing - if typeof(data[dataSet][key])==Bool - value = data[dataSet][key] - else - error("ERROR at reading the ",dataSet," yaml file: The value of the keyword ",key," is not correct. The value has to be of type Bool.") - end - else - value=false - println("WARNING at reading the ",dataSet," yaml file: The keyword ",key," or its value is missing. Therefore ",key,"=",value," is assumed and used.") + 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 - delete!(data[dataSet], key) - return value -end #function getBool! - -function getPositiveNumber!(data::Dict, dataSet::String, key::String, unit::String, required::Bool) - if haskey(data[dataSet],key) && data[dataSet][key]!=nothing - if typeof(data[dataSet][key]) <: Real && data[dataSet][key] >= 0.0 - value = data[dataSet][key] - else - error("ERROR at reading the ",dataSet," yaml file: The value of ",key," is no real floating point number >=0.0.") - end - elseif required - error("ERROR at reading the ",dataSet," yaml file: The keyword ",key," is missing. It has to be added with a value of type real floating point number >0.0.") - else - value=0.0 - println("WARNING at reading the ",dataSet," yaml file: The keyword ",key," is missing. Therefore ",key,"=0.0 ",unit," will be assumed and used." ) - end - delete!(data[dataSet], key) - - return value -end #function getPositiveNumber! - -function getString!(data::Dict, dataSet::String, key::String, validValues::Vector{String}) - # TODO change getString! to getSymbol! or to getEnum! ? - if haskey(data[dataSet],key) && data[dataSet][key]!=nothing - value = data[dataSet][key] - if typeof(value)==String - for validValue in validValues - if value == validValue - delete!(data[dataSet], key) - return value - end - end - end - error("ERROR at reading the ",dataSet," yaml file: The value of ",key," is wrong. It has to be one of the following String values: ", validValues) - else - error("ERROR at reading the ",dataSet," yaml file: The keyword ",key," is missing. It has to be added with one of the following String values: ", validValues) - end -end #function getString! -# second method of function getString! without validValues -function getString!(data::Dict, dataSet::String, key::String) - if haskey(data[dataSet],key) && data[dataSet][key]!=nothing - value = data[dataSet][key] - if typeof(value)==String - delete!(data[dataSet], key) - return value - end - error("ERROR at reading the ",dataSet," yaml file: The value of ",key," is wrong. It has to be of type String.") - else - error("ERROR at reading the ",dataSet," yaml file: The keyword ",key," is missing. It has to be added.") - end -end #function getString! - -function getTrainType!(data::Dict) - if haskey(data["train"],"trainType") && data["train"]["trainType"]!=nothing - if typeof(data["train"]["trainType"])==String && (data["train"]["trainType"]=="freight" || data["train"]["trainType"]=="motorCoachTrain" || data["train"]["trainType"]=="passenger") - # 01/05 old without enum: trainType=data["train"]["trainType"] # "passenger" or "freight" or "motorCoachTrain" - type = getEnum(data["train"]["trainType"], trainType) # "passenger" or "freight" or "motorCoachTrain" - delete!(data["train"], "trainType") - else - error("ERROR at reading the train yaml file: The value of trainType is wrong. It has to be freight, motorCoachTrain or passenger.") - end - else - error("ERROR at reading the train yaml file: The keyword trainType is missing. It has to be added with the value freight, motorCoachTrain or passenger.") - end - return type -end # function getTrainType! - -function getSpeedLimit!(data::Dict) # train's speed limit (in m/s) - v_limit_temp=0.0 - v_limit_kmh_temp=0.0 - if haskey(data["train"],"v_limit") && data["train"]["v_limit"]!=nothing - if typeof(data["train"]["v_limit"]) <: Real && data["train"]["v_limit"]>0.0 - v_limit_temp=data["train"]["v_limit"] # trains speed limit (in m/s) - delete!(data["train"], "v_limit") - else - error("ERROR at reading the train yaml file: The value of v_limit is no real floating point number >0.0.") - end - end - - if haskey(data["train"],"v_limit_kmh") && data["train"]["v_limit_kmh"]!=nothing - if typeof(data["train"]["v_limit_kmh"]) <: Real && data["train"]["v_limit_kmh"]>0.0 - v_limit_kmh_temp=data["train"]["v_limit_kmh"] # trains speed limit (in km/h) - delete!(data["train"], "v_limit_kmh") - else - error("ERROR at reading the train yaml file: The value of v_limit is no real floating point number >0.0.") - end - end - - if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0 - v_limit=v_limit_temp - difference=abs(v_limit_temp-v_limit_kmh_temp/3.6) - if difference >0.0 - println("WARNING at reading the train yaml file: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit," m/s is used." ) - end - elseif v_limit_temp > 0.0 - v_limit=v_limit_temp - elseif v_limit_kmh_temp > 0.0 - v_limit=v_limit_kmh_temp/3.6 - else - v_limit=1000.0/3.6 - println("WARNING at reading the train yaml file: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",v_limit," m/s is used." ) - end - - return v_limit -end #function getSpeedLimit! - -function get_a_braking!(data) - if haskey(data["train"],"a_braking") && data["train"]["a_braking"]!=nothing - if typeof(data["train"]["a_braking"]) <: Real - a_braking=data["train"]["a_braking"] - else - error("ERROR at reading the train yaml file: The value of the a_braking is no real floating point number <0.0.") - end - delete!(data["train"], "a_braking") - - if a_braking > 0.0 - a_braking =-a_braking - println("WARNING at reading the train yaml file: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking =",a_braking," m/s^2 is used." ) - elseif a_braking == 0.0 - error("ERROR at reading the train yaml file: The value for a_braking is 0.0. The braking acceleration has to be <0.0.") - end - else - error("ERROR at reading the train yaml file: The keyword a_braking is missing. It has to be added with a value of type real floating point number <0.0.") - end - - return a_braking -end #function get_a_braking! - -function getRotationMassFactors!(data::Dict, m_train::Real, m_t::Real, m_w::Real) - if haskey(data["train"],"rotationMassFactor_train") && data["train"]["rotationMassFactor_train"]!=nothing && typeof(data["train"]["rotationMassFactor_train"]) <: Real - if data["train"]["rotationMassFactor_train"]>0.0 - ξ_train=data["train"]["rotationMassFactor_train"] - ξ_t=0.0 - ξ_w=0.0 - else - error("ERROR at reading the train yaml file: The value of rotationMassFactor_train is no real floating point number >0.0.") - end - elseif haskey(data["train"],"rotationMassFactor_t") && data["train"]["rotationMassFactor_t"]!=nothing && typeof(data["train"]["rotationMassFactor_t"]) <: Real && (m_w==0.0 || (haskey(data["train"],"rotationMassFactor_w") && data["train"]["rotationMassFactor_w"]!=nothing && typeof(data["train"]["rotationMassFactor_w"]) <: Real)) - if data["train"]["rotationMassFactor_t"]>0.0 - ξ_t=data["train"]["rotationMassFactor_t"] - else - error("ERROR at reading the train yaml file: The value of rotationMassFactor_t is no real floating point number >0.0.") - end - if m_w>0.0 - if data["train"]["rotationMassFactor_w"]>=0.0 - ξ_w=data["train"]["rotationMassFactor_w"] - else - error("ERROR at reading the train yaml file: The value of rotationMassFactor_w is no real floating point number >=0.0.") - end - else - ξ_w=0.0 - end - ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train # rotation mass factor of the whole train (without unit) - else - error("ERROR at reading the train yaml file: The keywords rotationMassFactor_train or rotationMassFactor_t and rotationMassFactor_w are missing. They has to be added with a value of type real floating point number.") - end - delete!(data["train"], "rotationMassFactor_train") - delete!(data["train"], "rotationMassFactor_t") - delete!(data["train"], "rotationMassFactor_w") - - return (ξ_train, ξ_t, ξ_w) -end #function getRotationMassFactors! - -function getTractiveEffortVelocityPairs!(data::Dict) # pairs of velocity and tractive effort - if haskey(data["train"],"F_T_pairs") && data["train"]["F_T_pairs"]!=nothing - F_T_pairs=data["train"]["F_T_pairs"] - - tractiveEffortVelocityPairs=checkAndDefineTractiveEffortInput(F_T_pairs, 1.0) - - if haskey(data["train"],"F_T_pairs_kmh") && data["train"]["F_T_pairs_kmh"]!=nothing - println("WARNING at reading the train yaml file: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." ) - end - - elseif haskey(data["train"],"F_T_pairs_kmh") && data["train"]["F_T_pairs_kmh"]!=nothing - F_T_pairs_kmh=data["train"]["F_T_pairs_kmh"] - - tractiveEffortVelocityPairs=checkAndDefineTractiveEffortInput(F_T_pairs_kmh, 1000/3600) - else - error("ERROR at reading the train yaml file: There has to be one of the keywords F_T_pairs or F_T_pairs_kmh filled with a list of pairs of velocity and tractive effort.") - end # if - delete!(data["train"], "F_T_pairs") - delete!(data["train"], "F_T_pairs_kmh") - - return tractiveEffortVelocityPairs -end #function getTractiveEffortVelocityPairs! - -function checkAndDefineTractiveEffortInput(F_T_pairs, velocityMultiplier::AbstractFloat) - # TODO: check if its numbers are real ? function checkAndDefineTractiveEffortInput(F_T_pairs::Array{Array{Real,1},1}, velocityMultiplier::AbstractFloat) - # check if the elements of the array have the correct type - errorDetected=false - for row in 1:length(F_T_pairs) - if typeof(F_T_pairs[row][1]) <: Real && F_T_pairs[row][1]>=0.0 - else - errorDetected=true - println("ERROR at reading the train yaml file: The speed value of F_T_pairs in row ", row ," is no real floating point number >=0.0.") - end - if typeof(F_T_pairs[row][2]) <: Real && F_T_pairs[row][2]>=0.0 - else - errorDetected=true - println("ERROR at reading the train yaml file: The tractive effort value of F_T_pairs in row ", row ," is no real floating point number >=0.0.") - end - - if row>=2 && F_T_pairs[row][1] <= F_T_pairs[row-1][1] - errorDetected=true - println("ERROR at reading the train yaml file: The speed value of F_T_pairs in row ", row ," (v=",F_T_pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",F_T_pairs[row-1][1]," m/s).") - end - end # for - if errorDetected - error("ERROR at reading the train yaml file: 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 - tractiveEffortVelocityPairs=[] - if F_T_pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used - push!(tractiveEffortVelocityPairs, [0.0, F_T_pairs[1][2]]) - println("WARNING at reading the train yaml file: The tractive effort for v=0 m/s is missing. Therefore the first given value F_T(v=",F_T_pairs[1][1]," m/s)=",F_T_pairs[1][2]," N will be used." ) - end - - for row in 1:length(F_T_pairs) - push!(tractiveEffortVelocityPairs, [F_T_pairs[row][1]*velocityMultiplier, F_T_pairs[row][2]]) - end # for - - - if length(F_T_pairs[1])>2 - println("INFO according the train yaml file: Only the first two columns of F_T_pairs are used in this tool.") - end - return tractiveEffortVelocityPairs -end #function checkAndDefineTractiveEffortInput - -function get_Δv_w!(type::trainType) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - if type == passenger::trainType || type == motorCoachTrain::trainType - Δv_w=15.0/3.6 - elseif type == freight::trainType - Δv_w=0.0 - end # if - - #= 01/05 old without enum - if type=="passenger" || type=="motor coach train" - Δv_w=15.0/3.6 - elseif type== "freight" - Δv_w=0.0 - end # if =# - - return Δv_w -end #function get_Δv_w! - -function getSections!(data::Dict) - # read the section starting positions and corresponding information - if haskey(data["path"],"sectionStarts") && data["path"]["sectionStarts"]!=nothing - sectionStartsArray=data["path"]["sectionStarts"] - conversionFactor=1.0 # conversion factor between the units m/s and m/s - valueKey="sectionStarts" # necessary for error messages - - if haskey(data["path"],"sectionStarts") && data["path"]["sectionStarts_kmh"]!=nothing - println("WARNING at reading the path yaml file: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) - end - elseif haskey(data["path"],"sectionStarts_kmh") && data["path"]["sectionStarts_kmh"]!=nothing - sectionStartsArray=data["path"]["sectionStarts_kmh"] - conversionFactor=1/3.6 # conversion factor between the units km/h and m/s - valueKey="sectionStarts_kmh" # necessary for error messages - else - error("ERROR at reading the path yaml file: The keyword sectionStarts or sectionStarts_kmh is missing. It has to be added.") - end # if - delete!(data["path"], "sectionStarts") - delete!(data["path"], "sectionStarts_kmh") - - # 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 reading the path yaml file: The keyword ",valueKey," needs at least two rows for two points each with the three columns [s, v_limit, gradient].") - end - - for row in 1:length(sectionStartsArray) - if length(sectionStartsArray[row])>=3 - if length(sectionStartsArray[row])>3 - println("INFO at reading the path yaml file: Only the first three columns of sectionStartsArray are used in this tool.") - end - else - error("ERROR at reading the path yaml file: The keyword ",valueKey," needs to be filled with the three columns [s, v_limit, gradient].") - end - - if typeof(sectionStartsArray[row][1]) <: Real - if row > 1 - if sectionStartsArray[row][1]>sectionStartsArray[row-1][1] - else - errorDetected=true - println("ERROR at reading the path yaml file: The postion value of ",valueKey," in row ", row ," (",sectionStartsArray[row][1]," m) has to be higher than the value in the row above (",sectionStartsArray[row-1][1]," m).") - end - end - else - errorDetected=true - println("ERROR at reading the path yaml file: The position value (column 1) of ",valueKey," in row ", row ," is no real floating point number.") - end - - if typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2]>=0.0 - else - errorDetected=true - println("ERROR at reading the path yaml file: The speed limit (column 2) of ",valueKey," in row ", row ," is no real floating point number >=0.0.") - end - if typeof(sectionStartsArray[row][3]) <: Real - else - errorDetected=true - println("ERROR at reading the path yaml file: The tractive effort value (column 3) of ",valueKey," in row ", row ," is no real floating point number.") - end - end # for - if errorDetected - error("ERROR at reading the path yaml file: The values of ",valueKey," 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 - - return sections -end #function getSections! - -function addPointsOfInterest!(path::Dict, data::Dict) - # read the section starting positions and corresponding information - if haskey(data["path"],"pointsOfInterest") && data["path"]["pointsOfInterest"]!=nothing - pointsOfInterest = data["path"]["pointsOfInterest"] - delete!(data["path"], "pointsOfInterest") - - 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 reading the path yaml file: 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 reading the path yaml file: The point of interest in element ", element ," is no real floating point number.") - end - end # for - - if errorDetected - error("ERROR at reading the path yaml file: The values of the point of interest 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 - merge!(path, Dict(:pointsOfInterest => copiedPOIs)) - end - return (path, data) -end #function addPointsOfInterest! - -function informAboutUnusedKeys(data::Dict, dataSet::String) # inform the user which keywords of the imported data are not used in this tool - if length(data[dataSet])>0 - println("INFO at reading the ",dataSet," yaml file: The following Keywords are not used in this tool:") - for key in keys(data[dataSet]) - println(" - ",key) - end - end -end #function informAboutUnusedKeys - -""" - getEnum(string, enum_type) - -Converts a string to an enumerated type. -But only if the string matches an enumerated value. - -# Example -```jldoctest -julia> @enum trainType passenger freight - -julia> myTrain = "passenger" -"passenger" - -julia> myTrainType = getEnum(myTrain, trainType) -passenger::trainType = 0 -``` -""" -function getEnum(string::String, enum_type::DataType) - inst = instances(enum_type) # get all instances of enumerated type - syms = Symbol.(inst) # convert all instances to Symbols - lookup = Dict(zip(syms, inst)) # combine instances and Symbols in a lookup table - # - n_str = Symbol(string) # normalize String via a Symbol - return lookup[n_str] # return matched enumerated type -end # function getEnum + return dictionary +end # function importFromYaml end # module Input diff --git a/src/Input.jl b/src/Input.jl index 0fded9f..a21e5cb 100644 --- a/src/Input.jl +++ b/src/Input.jl @@ -5,14 +5,11 @@ # __copyright__ = "2022" # __license__ = "ISC" -# TODO: >>if train[:type] == freight::trainType<< does not work only in checkTrainType(train::Dict). because ::Main.TrainRun.Import.trainType != ::Main.TrainRun.Input.trainType -> why? checkTrainType ist therefore deactivated. -# TODO: adapt function informAboutUnusedKeys for Input.jl +# TODO: 2022-04-07: if EnergySaving should be used. The train type has do be defined and checked module Input export checkAndSetInput! -@enum trainType passenger=1 freight=2 motorCoachTrain=3 - approximationLevel = 6 # value for approximation to intersections TODO further explanation @@ -37,38 +34,53 @@ Read the train information from a YAML file, save it in a train Dict and return function checkAndSetTrain!(train::Dict) # check train information from input dictionary - checkString(train, "train", :name) # train's name - # TODO checkId ? train[:id] # train's identifier - checkTrainType(train) # passenger or freight or motorCoachTrain + 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" - checkPositiveNumber(train, "train", :length, "m") # total length (in m) + checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) + # TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) - checkAndSetPositiveNumber!(train, "train", :v_limit, "m/s", 1000.0/3.6) # train's speed limit (in m/s) - checkAndSet_a_braking!(train) # a_braking + checkAndSetSpeedLimit!(train) # train's speed limit (in m/s) + checkAndSetBrakingAcceleration!(train) # a_braking - checkPositiveNumber(train, "train", :m_td, "kg") # mass on the traction unit's driving axles (in kg) + 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") # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) + 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") # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) + 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 ‰) -# TODO: informAboutUnusedKeys(train, "train") # inform the user, which Symbols of the input dictionary are not used in this tool + # 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! @@ -76,12 +88,16 @@ end #function checkAndSetTrain! function checkAndSetPath!(path::Dict) # check path information from input dictionary - checkString(path, "path", :name) + checkAndSetString!(path, "path", :name, "") # TODO checkId ? path[:id] # path identifier checkAndSetSections!(path) checkAndSetPOIs!(path) -# TODO: informAboutUnusedKeys(path, "path") # inform the user, which Symbols of the input dictionary are not used in this tool + # 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! @@ -91,211 +107,310 @@ end # function checkAndSetPath! function checkAndSetSettings!(settings::Dict) # check settings information from input dictionary - checkString(settings, "settings", :massModel, ["mass point", "homogeneous strip"]) # model type of the train's mass "mass point" or "homogeneous strip" - checkString(settings, "settings", :stepVariable, ["s in m", "t in s", "v in m/s"]) # step variable of the step method "s in m", "t in s" or "v in m/s" - checkPositiveNumber(settings, "settings", :stepSize, "("*settings[:stepVariable]*")") # step size (unit depends on stepVariable: s in m, t in s and v in m/s) - checkAndSetBool!(settings, "settings", :operationModeMinimumRunningTime) # operation mode "minimum running time" - checkAndSetBool!(settings, "settings", :operationModeMinimumEnergyConsumption) # operation mode "minimum energy consumption" - checkString(settings, "settings", :typeOfOutput, ["julia dictionary", "CSV"]) # output as "julia dictionary" or as "CSV" + checkAndSetString!(settings, "settings", :massModel, "mass point", ["mass point", "homogeneous strip"]) # model type of the train's mass "mass point" or "homogeneous strip" + checkAndSetString!(settings, "settings", :stepVariable, "s in m", ["s in m", "t in s", "v in m/s"]) # step variable of the step method "s in m", "t in s" or "v in m/s" + + checkAndSetPositiveNumber!(settings, "settings", :stepSize, "("*settings[:stepVariable]*")", getDefaultStepSize(settings[:stepVariable])) # step size (unit depends on stepVariable: s in m, t in s and v in m/s) + checkAndSetBool!(settings, "settings", :operationModeMinimumRunningTime, true) # operation mode "minimum running time" + checkAndSetBool!(settings, "settings", :operationModeMinimumEnergyConsumption, false) # operation mode "minimum energy consumption" + checkAndSetString!(settings, "settings", :typeOfOutput, "julia dictionary", ["julia dictionary", "CSV"]) # output as "julia dictionary" or as "CSV" if settings[:typeOfOutput] == "CSV" - checkString(settings, "settings", :csvDirectory) + checkAndSetString!(settings, "settings", :csvDirectory, "~/Desktop/TrainRun") + # TODO use correct default directory # TODO: it could be checked if the path is existing on the pc end # if - checkString(settings, "settings", :detailOfOutput, ["running time", "points of interest", "driving course", "everything"]) # should the output be only the value of the "running time", or an array of "points of interest" or the complete "driving course" as array or a dictionary with "everything"? - # 30/31 old: checkString(settings, "settings", :detailOfOutput, ["minimal", "points of interest", "driving course"]) # should the output be "minimal" or are "points of interest" or the complete "driving course" required or also all the information about the different sections? + checkAndSetString!(settings, "settings", :detailOfOutput, "running time", ["running time", "points of interest", "driving course", "everything"]) # should the output be only the value of the "running time", or an array of "points of interest" or the complete "driving course" as array or a dictionary with "everything"? -# TODO: informAboutUnusedKeys(settings, "settings") # inform the user, which Symbols of the input dictionary are not used in this tool + # inform the user about keys of the input dictionary that are not used in this tool + usedKeys = [:massModel, :stepVariable, :stepSize, + :operationModeMinimumRunningTime, :operationModeMinimumEnergyConsumption, + :typeOfOutput, :detailOfOutput] + if settings[:typeOfOutput] == "CSV" + push!(usedKeys, :csvDirectory) + end + informAboutUnusedKeys(collect(keys(settings)), usedKeys::Vector{Symbol}, "settings") return settings end # function checkAndSetSettings! -function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol) +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 dictionary for the ",dictionaryType,": The value of the Symbol :",String(key)," is not correct. The value has to be of type Bool.") + 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 => false)) - println("WARNING at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.") + 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 checkPositiveNumber(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0 - return true - else - error("ERROR at checking the input dictionary for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") - end - else - error("ERROR at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," is missing. It has to be added with a value of type real floating point number >0.0.") - end - return false -end #function checkPositiveNumber - 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 dictionary for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") + 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("WARNING at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) + 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! -function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String) +# 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^approximationLevel) # 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^approximationLevel) # 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 dictionary for the ",dictionaryType,": The value of ",String(key)," is no real number.") + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.") end else - if key == :Δv_t - value = 15.0/3.6 - elseif key == :Δv_w - if dictionary[:type] == passenger::trainType || dictionary[:type] == motorCoachTrain::trainType - value = 15.0/3.6 - elseif dictionary[:type] == freight::trainType - value = 0.0 - end # if - else - value = 0.0 - end - merge!(dictionary, Dict(key => value)) - println("WARNING at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," is missing. Therefore ",String(key),"=",value," ",unit," will be assumed and used." ) + 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 checkString(dictionary::Dict, dictionaryType::String, key::Symbol, validValues::Vector{String}) - # TODO change getString! to getSymbol! or to getEnum! ? - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value)==String - for validValue in validValues - if value == validValue - return true - end - end - end - error("ERROR at checking the input dictionary for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues) - else - error("ERROR at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," is missing. It has to be added with one of the following String values: ", validValues) - end - return false -end #function checkString -# second method of function checkString without validValues -function checkString(dictionary::Dict, dictionaryType::String, key::Symbol) - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value)==String - return true - end - error("ERROR at checking the input dictionary for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.") - else - error("ERROR at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(key)," is missing. It has to be added.") - end - return false -end #function checkString - -function checkTrainType(train::Dict) - if haskey(train,:type) && train[:type]!=nothing -#= # println("train[:type]=",train[:type]," typeof(train[:type])=", typeof(train[:type])) - if train[:type] == passenger::trainType || train[:type] == freight::trainType || train[:type] == motorCoachTrain::trainType - - return true - else - error("ERROR at checking the input dictionary for the train: The value of train[:type] is wrong. It has to be passenger, freight or motorCoachTrain of enum type trainType.") - end =# - else - error("ERROR at checking the input dictionary for the train: The Symbol :type is missing. It has to be added with the value passenger, freight or motorCoachTrain of enum type trainType.") - end - return false -end # function checkTrainType - -function checkAndSet_a_braking!(train::Dict) - if haskey(train, :a_braking) && train[:a_braking]!=nothing - if typeof(train[:a_braking]) <: Real - return train - else - error("ERROR at checking the input dictionary for the train: The value of the a_braking is no real floating point number <0.0.") - end - - if train[:a_braking] > 0.0 - train[:a_braking] =-train[:a_braking] - println("WARNING at checking the input dictionary 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 dictionary 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 dictionary for the train: The Symbol :a_braking is missing. It has to be added with a value of type real floating point number <0.0.") - end - - return train -end #function checkAndSet_a_braking! - 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^approximationLevel) - error("ERROR at checking the input dictionary for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") + 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 dictionary for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.") + 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("WARNING at checking the input dictionary for the ",dictionaryType,": The Symbol :",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." ) + 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 checkAndSetRotationMassFactors!(train::Dict) - if haskey(train, :ξ_train) && train[:ξ_train]!=nothing && typeof(train[:ξ_train]) <: Real - if train[:ξ_train]>0.0 - if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && typeof(train[:ξ_t]) <: Real && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing && typeof(train[:ξ_w]) <: Real)) - ## println("train[:ξ_train]=",train[:ξ_train]," Rest=",(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train]) - # println("train[:ξ_t]=",train[:ξ_t]," train[:m_t]=",train[:m_t]," train[:ξ_w]=",train[:ξ_w]," train[:m_w]=",train[:m_w]," train[:m_train]=",train[:m_train]) +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! - # println("absDiff=", abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train])) +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^approximationLevel) # 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^approximationLevel) - error("ERROR at checking the input dictionary for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") + 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 dictionary for the train: The value of :ξ_train is no real floating point number >0.0.") + error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.") end - elseif haskey(train, :ξ_t) && train[:ξ_t]!=nothing && typeof(train[:ξ_t]) <: Real && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing && typeof(train[:ξ_w]) <: Real)) - if train[:ξ_t]>0.0 - ξ_t=train[:ξ_t] - else - error("ERROR at checking the input dictionary for the train: The value of :ξ_t is no real floating point number >0.0.") - end - if train[:m_w]>0.0 - if train[:ξ_w]>=0.0 - ξ_w=train[:ξ_w] - else - error("ERROR at checking the input dictionary for the train: The value of :ξ_w is no real floating point number >=0.0.") - end - else - ξ_w=0.0 - end - - ξ_train=(ξ_t*train[:m_t] + ξ_w*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit) - merge!(train, Dict(:ξ_train => ξ_train)) else - error("ERROR at checking the input dictionary for the train: The Symbols :ξ_train or :ξ_t and :ξ_w are missing. They has to be added with a value of type real floating point number.") + 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 @@ -304,118 +419,259 @@ 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 - # check if the elements of the array have the correct type - errorDetected=false + 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." ) - 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 dictionary 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 dictionary for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end + 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." ) - if row>=2 && pairs[row][1] <= pairs[row-1][1] - errorDetected=true - println("ERROR at checking the input dictionary 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 + 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 - if errorDetected - error("ERROR at checking the input dictionary 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("WARNING at checking the input dictionary 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)) - 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 else - error("ERROR at checking the input dictionary for the train: There has to be the Symbol :tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.") + 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::Dict) # check the section information if haskey(path,:sections) && path[:sections]!=nothing - sections = path[:sections] + # 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." ) - checkedSections = [] - increasing = false - decreasing = false + 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." ) - #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in checkTractiveEffortVelocityPairs!? - # check values for section==1 - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m") # first point of the section (in m) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m") # 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, "‰") # specific path resistance of the section (in ‰) + 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) - push!(checkedSections, sections[1]) + 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 - if sections[1][:s_start] < sections[1][:s_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[1][:s_start] > sections[1][:s_end] + elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] decreasing = true else pop!(checkedSections) - println("WARNING at checking the input dictionary 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.") + 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 - for section in 2:length(sections) - checkAndSetRealNumber!(sections[section], "path[:sections]["*string(section)*"]", :s_start, "m") # first point of the section (in m) - checkAndSetRealNumber!(sections[section], "path[:sections]["*string(section)*"]", :s_end, "m") # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[section], "path[:sections]["*string(section)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[section], "path[:sections]["*string(section)*"]", :f_Rp, "‰") # specific path resistance of the section (in ‰) - - push!(checkedSections, sections[section]) - - # compare the section's start and end position - if sections[section][:s_start] < sections[section][:s_end] - increasing = true - elseif sections[section][:s_start] > sections[section][:s_end] - decreasing = true - else - pop!(checkedSections) - println("WARNING at checking the input dictionary for the path: The ",section,". 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 dictionary for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",section,". section differs from the previous.") - end - - - if length(checkedSections)>1 && sections[section][:s_start] != checkedSections[end-1][:s_end] - error("ERROR at checking the input dictionary for the path[:sections]: The starting position of the ",section,". section (s=",sections[section][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") - # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? - end - end #for - else - error("ERROR at checking the input dictionary 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.") - 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::Dict, 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 + 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::Dict) # read the section starting positions and corresponding information - if haskey(path,:pointsOfInterest) + if haskey(path, :pointsOfInterest) if path[:pointsOfInterest] != nothing pointsOfInterest = path[:pointsOfInterest] @@ -426,17 +682,17 @@ function checkAndSetPOIs!(path::Dict) if element > 1 if pointsOfInterest[element] < pointsOfInterest[element-1] sortingNeeded = true - println("INFO at checking the input dictionary 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.") + 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 dictionary for the path: The point of interest in element ", element ," is no real floating point number.") + 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 dictionary for the path: The values of pointsOfInterest have to be corrected.") + error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") end if sortingNeeded == true sort!(pointsOfInterest) @@ -453,7 +709,7 @@ function checkAndSetPOIs!(path::Dict) path[:pointsOfInterest] = copiedPOIs else - println("INFO at checking the input dictionary for the path: The key pointsOfInterest exists but without values.") + println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") delete!(path, :pointsOfInterest) end end @@ -461,13 +717,41 @@ function checkAndSetPOIs!(path::Dict) 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 -#= if length(dictionary)>0 - println("INFO at checking the input dictionary for the ",dictionaryType,": The following Keywords are not used in this tool:") - for key in keys(dictionary) +function getDefaultStepSize(stepVariable::String) + if stepVariable == "s in m" + return 10.0 + elseif stepVariable == "t in s" + return 3.0 + elseif stepVariable == "v in m/s" + return 0.1 + #else + # error("ERROR at getting a default step size. The step variable ",stepVariable," can not be used.") + end +end #function getDefaultStepSize + +#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 end #function informAboutUnusedKeys end # module Input diff --git a/src/TrainRun.jl b/src/TrainRun.jl index b49f885..c811985 100644 --- a/src/TrainRun.jl +++ b/src/TrainRun.jl @@ -1,3 +1,10 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + module TrainRun # include main module TrainRunCalc @@ -26,7 +33,7 @@ using .EnergySaving export calculateDrivingDynamics, # import functions -importYamlFiles, importYamlFile, +importYamlFiles, importFromYaml, # functions for saving energy that are not recommended to use in this state addOperationModeEnergySaving!,