From c5a339e403bca9184fe347b9e349032a9ac266c3 Mon Sep 17 00:00:00 2001 From: Max Kannenberg <95709892+MaxKannenberg@users.noreply.github.com> Date: Tue, 18 Jan 2022 13:39:51 +0100 Subject: [PATCH] Divide TrainRun into TrainRunCalc and Import --- examples/ExtendedWorkingExample.jl | 33 +- examples/MinimalWorkingExample.jl | 7 +- src/Import.jl | 540 +++++++++++++++ src/Input.jl | 1003 ++++++++++------------------ src/TrainRun.jl | 89 +-- src/TrainRunCalc.jl | 78 +++ 6 files changed, 1024 insertions(+), 726 deletions(-) create mode 100644 src/Import.jl create mode 100644 src/TrainRunCalc.jl diff --git a/examples/ExtendedWorkingExample.jl b/examples/ExtendedWorkingExample.jl index 583b1cf..8c27f81 100644 --- a/examples/ExtendedWorkingExample.jl +++ b/examples/ExtendedWorkingExample.jl @@ -9,31 +9,28 @@ include("../src/TrainRun.jl") using .TrainRun allPaths=[] -push!(allPaths, "data/paths/path_1_10km_nConst_vConst.yaml") -push!(allPaths, "data/paths/path_2_10km_nVar_vConst.yaml") -push!(allPaths, "data/paths/path_3_10km_nConst_vVar.yaml") -push!(allPaths, "data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml") +push!(allPaths, importYamlFile(:path, "data/paths/path_1_10km_nConst_vConst.yaml")) +push!(allPaths, importYamlFile(:path, "data/paths/path_2_10km_nVar_vConst.yaml")) +push!(allPaths, importYamlFile(:path, "data/paths/path_3_10km_nConst_vVar.yaml")) +push!(allPaths, importYamlFile(:path, "data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml")) + allSettings=[] -push!(allSettings, "data/settings.yaml") +push!(allSettings, importYamlFile(:settings, "data/settings.yaml")) allTrains=[] -push!(allTrains, "data/trains/train_freight_V90withOreConsist.yaml") -push!(allTrains, "data/trains/train_passenger_SiemensDesiroClassic.yaml") -push!(allTrains, "data/trains/train_passenger_IC2.yaml") +push!(allTrains, importYamlFile(:train, "data/trains/train_freight_V90withOreConsist.yaml")) +push!(allTrains, importYamlFile(:train, "data/trains/train_passenger_SiemensDesiroClassic.yaml")) +push!(allTrains, importYamlFile(:train, "data/trains/train_passenger_IC2.yaml")) -for pathDirectory in allPaths +for path in allPaths # println(" - - - - - - - - -") - # println("path: ", pathDirectory) - for trainDirectory in allTrains - # println("train: ", trainDirectory) - for settingsDirectory in allSettings - testDict=calculateDrivingDynamics(trainDirectory, pathDirectory, settingsDirectory) - + # println("path: ", path) + for train in allTrains + # println("train: ", train) + for settings in allSettings + testDict=calculateDrivingDynamics(train, path, settings) sleep(2) - - # println("") - # println("") # println("") end end diff --git a/examples/MinimalWorkingExample.jl b/examples/MinimalWorkingExample.jl index 92b4954..6e22185 100644 --- a/examples/MinimalWorkingExample.jl +++ b/examples/MinimalWorkingExample.jl @@ -8,9 +8,10 @@ include("../src/TrainRun.jl") using .TrainRun -train = "data/trains/train_freight_V90withOreConsist.yaml" -running_path = "data/paths/path_1_10km_nConst_vConst.yaml" -settings = "data/settings.yaml" +train_directory = "data/trains/train_freight_V90withOreConsist.yaml" +running_path_directory = "data/paths/path_1_10km_nConst_vConst.yaml" +setting_directory = "data/settings.yaml" +(train, running_path, settings) = importYamlFiles(train_directory, running_path_directory, setting_directory) train_run = calculateDrivingDynamics(train, running_path, settings) runtime = last(train_run[:outputArrayMinimumRunningTime])[5] diff --git a/src/Import.jl b/src/Import.jl new file mode 100644 index 0000000..21a9a5d --- /dev/null +++ b/src/Import.jl @@ -0,0 +1,540 @@ +module Import + +import YAML + +export importYamlFiles, importYamlFile + +@enum trainType passenger=1 freight=2 motorCoachTrain=3 + +""" +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) + + 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. + """ +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") + 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) + + 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", ["minimal", "driving course"]) # should the output be "minimal" or "driving course" + + 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.") + 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 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 + +end # module Input diff --git a/src/Input.jl b/src/Input.jl index c4f09e6..c6ba71f 100644 --- a/src/Input.jl +++ b/src/Input.jl @@ -1,691 +1,412 @@ +# 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 module Input -import YAML -using ..types - -export readInput +export checkAndSetInput! @enum trainType passenger=1 freight=2 motorCoachTrain=3 + +approximationLevel = 6 # value for approximation to intersections TODO further explanation + """ Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. """ -function readInput(trainDirectory::String, pathDirectory::String, settingsDirectory::String) - train=inputTrain(trainDirectory) - path=inputPath(pathDirectory) - settings=inputSettings(settingsDirectory) +function checkAndSetInput!(train::Dict, path::Dict, settings::Dict) + checkAndSetTrain!(train) + checkAndSetPath!(path) + checkAndSetSettings!(settings) return (train, path, settings) - end #function readInput +end #function checkAndSetInput! """ Read the train information from a YAML file, save it in a train Dict and return it. """ -function inputTrain(trainDirectory::String) - data = YAML.load(open(trainDirectory)) - #collect(keys(data)) - #collect(values(data)) +function checkAndSetTrain!(train::Dict) + # check train information from input dictionary - if haskey(data["train"],"name") - name=data["train"]["name"] # train's name - delete!(data["train"], "name") - else - error("ERROR at reading the train yaml file: The keyword name is missing. It has to be added.") - end + checkString(train, "train", :name) # train's name + # TODO checkId ? train[:id] # train's identifier + checkTrainType(train) # passenger or freight or motorCoachTrain - id=1 # trains identifier + checkPositiveNumber(train, "train", :length, "m") # total length (in m) - if haskey(data["train"],"trainType") - # @enum trainType passenger=1 freight=2 motorCoachTrain=3 - if typeof(data["train"]["trainType"])==String && (data["train"]["trainType"]=="freight" || data["train"]["trainType"]=="motorCoachTrain" || data["train"]["trainType"]=="passenger") - # 01/05 old without enum: if typeof(data["train"]["trainType"])==String && (data["train"]["trainType"]=="freight" || data["train"]["trainType"]=="motor coach train" || data["train"]["trainType"]=="passenger") - # 01/05 old without enum: trainType=data["train"]["trainType"] # "passenger" or "freight" or "motor coach train" - 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 + checkAndSetPositiveNumber!(train, "train", :v_limit, "m/s", 1000.0/3.6) # train's speed limit (in m/s) + checkAndSet_a_braking!(train) # a_braking - if haskey(data["train"],"l_train") - if typeof(data["train"]["l_train"]) <: Real && data["train"]["l_train"]>0.0 - trainLength=data["train"]["l_train"] # total length (in m) - delete!(data["train"], "l_train") - else - error("ERROR at reading the train yaml file: The value of the length is no real number >0.0.") - end - else - error("ERROR at reading the train yaml file: The keyword length is missing. It has to be added with a value of type real floating point number >0.0.") - end + checkPositiveNumber(train, "train", :m_td, "kg") # 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) - # speed limit: # trains 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/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 - - - # a_braking - if haskey(data["train"],"a_braking") - 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 - - # mass on the traction units driving axles (in kg) - if haskey(data["train"],"m_td") - if typeof(data["train"]["m_td"]) <: Real && data["train"]["m_td"]>0.0 - m_td=data["train"]["m_td"] - else - error("ERROR at reading the train yaml file: The value of m_td is no real floating point number >0.0.") - end - else - error("ERROR at reading the train yaml file: The keyword m_td is missing. It has to be added with a value of type real floating point number >0.0.") - end - delete!(data["train"], "m_td") - - - # mass on the traction units carrying axles (in kg) - if haskey(data["train"],"m_tc") - if typeof(data["train"]["m_tc"]) <: Real && data["train"]["m_tc"]>=0.0 - m_tc=data["train"]["m_tc"] - else - error("ERROR at reading the train yaml file: The value of m_tc is no real floating point number >=0.0.") - end - else - error("ERROR at reading the train yaml file: The keyword m_tc is missing. It has to be added with a value of type real floating point number >=0.0.") - end - delete!(data["train"], "m_tc") - - # mass of the traction unit (in kg) - m_t=m_td+m_tc - - - # mass of the set of wagons (consist) (in kg) - if haskey(data["train"],"m_w") - if typeof(data["train"]["m_w"]) <: Real && data["train"]["m_w"]>=0.0 - m_w=data["train"]["m_w"] - else - error("ERROR at reading the train yaml file: The value of m_w is no real floating point number >=0.0.") - end - else - m_w=0.0 - println("WARNING at reading the train yaml file: The keyword m_w is missing. Therefore m_w =",m_w," kg is used.") - end - delete!(data["train"], "m_w") - - # total mass (in kg) - m_train=m_t+m_w - - if haskey(data["train"],"rotationMassFactor_train") && 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") && typeof(data["train"]["rotationMassFactor_t"]) <: Real && (m_w==0.0 || (haskey(data["train"],"rotationMassFactor_w") && 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") - - - # 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") - + checkAndSetRotationMassFactors!(train) + checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort # coefficients for the vehicle resistance of the traction unit - - # coefficient for velocitiy difference between traction unit and outdoor air - Δv_t=15.0/3.6 - - # coefficient for basic resistance due to the traction units driving axles (in ‰) - if haskey(data["train"],"f_Rtd0") && data["train"]["f_Rtd0"]!=nothing - if typeof(data["train"]["f_Rtd0"]) <: Real && data["train"]["f_Rtd0"]>=0.0 - f_Rtd0=data["train"]["f_Rtd0"] - else - error("ERROR at reading the train yaml file: The value of f_Rtd0 is no real floating point number >=0.0.") - end - else - f_Rtd0=0.0 - println("WARNING at reading the train yaml file: The keyword f_Rtd0 is missing. Therefore f_Rtd0=0.0 ‰ will be assumed and used." ) - end - delete!(data["train"], "f_Rtd0") - - # coefficient for basic resistance due to the traction units carring axles (in ‰) - if haskey(data["train"],"f_Rtc0") && data["train"]["f_Rtc0"]!=nothing - if typeof(data["train"]["f_Rtc0"]) <: Real && data["train"]["f_Rtc0"]>=0.0 - f_Rtc0=data["train"]["f_Rtc0"] - else - error("ERROR at reading the train yaml file: The value of f_Rtc0 is no real floating point number >=0.0.") - end - else - f_Rtc0=0.0 - println("WARNING at reading the train yaml file: The keyword f_Rtc0 is missing. Therefore f_Rtc0=0.0 ‰ will be assumed and used." ) - end - delete!(data["train"], "f_Rtc0") - - # coefficient for air resistance of the traction units (in N) - if haskey(data["train"],"F_Rt2") && data["train"]["F_Rt2"]!=nothing - if typeof(data["train"]["F_Rt2"]) <: Real && data["train"]["F_Rt2"]>=0.0 - F_Rt2=data["train"]["F_Rt2"] - else - error("ERROR at reading the train yaml file: The value of F_Rt2 is no real floating point number >=0.0.") - end - else - F_Rt2=0.0 - println("WARNING at reading the train yaml file: The keyword F_Rt2 is missing. Therefore F_Rt2=0.0 N will be assumed and used." ) - end - delete!(data["train"], "F_Rt2") - - + checkAndSetRealNumber!(train, "train", :Δv_t, "m/s") # 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) + 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 ‰) - # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) -# if type == getEnum("passenger", trainType) || type == getEnum("motorCoachTrain", trainType) - if type == passenger::trainType || type == motorCoachTrain::trainType - # TODO: if type == trainType(:passenger) || type == trainType(:motorCoachTrain) - # TODO: ODER if trainType(type) == trainType(:passenger) || trainType(type) == trainType(:motorCoachTrain) - Δv_w=15.0/3.6 -# elseif type == getEnum("freight", trainType) - elseif type == freight::trainType - Δv_w=0.0 - end # if - - #= 01/05 old without enum - # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - if trainType=="passenger" || trainType=="motor coach train" - Δv_w=15.0/3.6 - elseif trainType== "freight" - Δv_w=0.0 - end # if - =# - - # coefficient for basic resistance of the set of wagons (consist) (in ‰) - if haskey(data["train"],"f_Rw0") && data["train"]["f_Rw0"]!=nothing - if typeof(data["train"]["f_Rw0"]) <: Real && data["train"]["f_Rw0"]>=0.0 - f_Rw0=data["train"]["f_Rw0"] - else - error("ERROR at reading the train yaml file: The value of f_Rw0 is no real floating point number >=0.0.") - end - else - f_Rw0=0.0 - println("WARNING at reading the train yaml file: The keyword f_Rw0 is missing. Therefore f_Rw0=0.0 ‰ will be assumed and used." ) - end - delete!(data["train"], "f_Rw0") - - # coefficient for basic resistance of the set of wagons (consist) (in ‰) - if haskey(data["train"],"f_Rw1") && data["train"]["f_Rw1"]!=nothing - if typeof(data["train"]["f_Rw1"]) <: Real && data["train"]["f_Rw1"]>=0.0 - f_Rw1=data["train"]["f_Rw1"] - else - error("ERROR at reading the train yaml file: The value of f_Rw1 is no real floating point number >=0.0.") - end - else - f_Rw1=0.0 - println("WARNING at reading the train yaml file: The keyword f_Rw1 is missing. Therefore f_Rw1=0.0 ‰ will be assumed and used." ) - end - delete!(data["train"], "f_Rw1") - - # coefficient for basic resistance of the set of wagons (consist) (in ‰) - if haskey(data["train"],"f_Rw2") && data["train"]["f_Rw2"]!=nothing - if typeof(data["train"]["f_Rw2"]) <: Real && data["train"]["f_Rw2"]>=0.0 - f_Rw2=data["train"]["f_Rw2"] - else - error("ERROR at reading the train yaml file: The value of f_Rw2 is no real floating point number >=0.0.") - end - else - f_Rw2=0.0 - println("WARNING at reading the train yaml file: The keyword f_Rw2 is missing. Therefore f_Rw2=0.0 ‰ will be assumed and used." ) - end - delete!(data["train"], "f_Rw2") - - - # inform the user, which keywords of the input data are not used in this tool: - if length(data["train"])>0 - println("INFO at reading the train yaml file: The following Keywords are not used in this tool:") - for key in keys(data["train"]) - println(" - ",key) - end - end - - # 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 :trainType => trainType, # 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 - - # 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) +# informAboutUnusedKeys(train, "train") # inform the user, which Symbols of the input dictionary are not used in this tool return train -end #function inputTrain +end #function checkAndSetTrain! -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 +function checkAndSetPath!(path::Dict) + # check path information from input dictionary - 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 + checkString(path, "path", :name) + # TODO checkId ? path[:id] # path identifier + checkAndSetSections!(path) - # 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 +# TODO: informAboutUnusedKeys(path, "path") # inform the user, which Symbols of the input dictionary are not used in this tool - 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 inputPath(pathDirectory::String) - # read path information from a YAML file, save it in a path Dict and return it - data = YAML.load(open(pathDirectory)) - #collect(keys(data)) - #collect(values(data)) - - - if haskey(data["path"],"name") && data["path"]["name"]!=nothing - name=data["path"]["name"] # path name - else - error("ERROR at reading the path yaml file: The keyword name is missing. It has to be added.") - end - delete!(data["path"], "name") - - id=1 # path identifier - - # 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["train"],"sectionStarts") && data["train"]["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 plausible values - errorDetected=false - if length(sectionStartsArray)>=2 - else - 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 - - # save values in the path Dict - 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 - - path = Dict(:name => name, - :id => id, - :sections => sections) - - # inform the user which keywords of the input data are not used in this tool: - if length(data["path"])>0 - println("INFO at reading the path yaml file: The following Keywords are not used in this tool:") - for key in keys(data["path"]) - println(" - ",key) - end - end return path -end # function inputPath - +end # function checkAndSetPath! ## settings for the calculation -function inputSettings(settingsDirectory::String) - # read setting information from a YAML file, save it in a settings Dict and return it - data = YAML.load(open(settingsDirectory)) - #collect(keys(data)) - #collect(values(data)) +function checkAndSetSettings!(settings::Dict) + # check settings information from input dictionary - # initialize the settings Dictionary - settings = Dict(:massModel => "", # model type of the unions 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 steapVariable 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" + 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" - # model type of the train's mass "mass point" or "homogeneous strip" - if haskey(data["settings"],"massModel") - if typeof(data["settings"]["massModel"])==String && (data["settings"]["massModel"]=="mass point" || data["settings"]["massModel"]=="homogeneous strip") - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:massModel => data["settings"]["massModel"])) # "mass point" or "homogeneous strip" - settings[:massModel] = data["settings"]["massModel"] # "mass point" or "homogeneous strip" - else - error("ERROR at reading the settings yaml file: The value of massModel is wrong. It has to be mass point or homogeneous strip.") - end - else - error("ERROR at reading the settings yaml file: The keyword massModel is missing. It has to be added with the value mass point or homogeneous strip.") - end - delete!(data["settings"], "massModel") - - - # step variable of the step method "s in m", "t in s" or "v in m/s" - if haskey(data["settings"],"stepVariable") - if typeof(data["settings"]["stepVariable"])==String && (data["settings"]["stepVariable"]=="s in m" || data["settings"]["stepVariable"]=="t in s" || data["settings"]["stepVariable"]=="v in m/s") - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:stepVariable => data["settings"]["stepVariable"])) # "s in m", "t in s" or "v in m/s" - settings[:stepVariable] = data["settings"]["stepVariable"] # "s in m", "t in s" or "v in m/s" - else - error("ERROR at reading the settings yaml file: The value of stepVariable is wrong. It has to be s in m, t in s or v in m/s.") - end - else - error("ERROR for the settings yaml file: The keyword stepVariable is missing. It has to be added with the value s in m, t in s or v in m/s.") - end - delete!(data["settings"], "stepVariable") - - - # step size (unit depends on steapVariable: s in m, t in s and v in m/s) - if haskey(data["settings"],"stepSize") - if typeof(data["settings"]["stepSize"]) <: Real && data["settings"]["stepSize"]>0.0 - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:stepSize => data["settings"]["stepSize"])) - settings[:stepSize] = data["settings"]["stepSize"] - else - error("ERROR at reading the settings yaml file: The value of the stepSize is no real floating point number >0.0.") - end - else - error("ERROR at reading the settings yaml file: The keyword stepSize is missing. It has to be added with a value of type real floating point number >0.0.") - end - delete!(data["settings"], "stepSize") - - - # operation mode "minimum running time" - if haskey(data["settings"],"operationModeMinimumRunningTime") && data["settings"]["operationModeMinimumRunningTime"]!=nothing - if typeof(data["settings"]["operationModeMinimumRunningTime"])==Bool - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:operationModeMinimumRunningTime => data["settings"]["operationModeMinimumRunningTime"])) - settings[:operationModeMinimumRunningTime] = data["settings"]["operationModeMinimumRunningTime"] - else - error("ERROR at reading the settings yaml file: The value of the keyword operationModeMinimumRunningTime is not correct. The value has to be of type boolean.") - end - else - settings[:operationModeMinimumRunningTime]=false - println("WARNING at reading the settings yaml file: The keyword operationModeMinimumRunningTime or its value is missing. Therefore operationModeMinimumRunningTime=",settings[:operationModeMinimumRunningTime]," is assumed and used.") - end - delete!(data["settings"], "operationModeMinimumRunningTime") - - - # operation mode "minimum energy consumption" - if haskey(data["settings"],"operationModeMinimumEnergyConsumption") && data["settings"]["operationModeMinimumEnergyConsumption"]!=nothing - if typeof(data["settings"]["operationModeMinimumEnergyConsumption"])==Bool - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:operationModeMinimumEnergyConsumption => data["settings"]["operationModeMinimumEnergyConsumption"])) - settings[:operationModeMinimumEnergyConsumption] = data["settings"]["operationModeMinimumEnergyConsumption"] - else - error("ERROR at reading the settings yaml file: The value of the keyword operationModeMinimumEnergyConsumption is not correct. The value has to be of type boolean.") - end - else - settings[:operationModeMinimumEnergyConsumption] = false - println("WARNING at reading the settings yaml file: The keyword operationModeMinimumEnergyConsumption or its value is missing. Therefore operationModeMinimumEnergyConsumption=", settings[:operationModeMinimumEnergyConsumption]," is assumed and used.") - end - delete!(data["settings"], "operationModeMinimumEnergyConsumption") - - # output as "julia dictionary" or as "CSV" - if haskey(data["settings"],"typeOfOutput") - if typeof(data["settings"]["typeOfOutput"])==String && (data["settings"]["typeOfOutput"]=="julia dictionary" || data["settings"]["typeOfOutput"]=="CSV") - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:typeOfOutput => data["settings"]["typeOfOutput"])) # "julia dictionary" or "CSV" - settings[:typeOfOutput] = data["settings"]["typeOfOutput"] # "julia dictionary" or "CSV" - else - error("ERROR at reading the settings yaml file: The value of typeOfOutput is wrong. It has to be julia dictionary or CSV.") - end - else - error("ERROR at reading the settings yaml file: The keyword typeOfOutput is missing. It has to be added with the value julia dictionary or CSV.") - end - delete!(data["settings"], "typeOfOutput") - - # TODO: it could be checked if the path is existing on the pc if settings[:typeOfOutput] == "CSV" - if haskey(data["settings"],"csvDirectory") - if typeof(data["settings"]["csvDirectory"])==String - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:csvDirectory => data["settings"]["csvDirectory"])) - settings[:csvDirectory] = data["settings"]["csvDirectory"] - else - error("ERROR at reading the settings yaml file: The value of csvDirectory is wrong. It has to be of type String.") - end - else - error("ERROR at reading the settings yaml file: The keyword csvDirectory is missing. It has to be added.") - end - delete!(data["settings"], "csvDirectory") + checkString(settings, "settings", :csvDirectory) + # TODO: it could be checked if the path is existing on the pc end # if + checkString(settings, "settings", :detailOfOutput, ["minimal", "driving course"]) # should the output be "minimal" or "driving course" - # should the output be "minimal" or "driving course" - if haskey(data["settings"],"detailOfOutput") - if typeof(data["settings"]["detailOfOutput"])==String && (data["settings"]["detailOfOutput"]=="minimal" || data["settings"]["detailOfOutput"]=="driving course") - # 12/15 old, not needed if already initialized: merge!(settings, Dict(:detailOfOutput => data["settings"]["detailOfOutput"])) # "minimal" or "driving course" - settings[:detailOfOutput] = data["settings"]["detailOfOutput"] - else - error("ERROR at reading the settings yaml file: The value of detailOfOutput is wrong. It has to be minimal or driving course.") - end - else - error("ERROR at reading the settings yaml file: The keyword detailOfOutput is missing. It has to be added with the value minimal or driving course.") - end - delete!(data["settings"], "detailOfOutput") - - - # inform the user, which keywords of the input data are not used in this tool: - if length(data["settings"])>0 - println("INFO at reading the settings yaml file: The following Keywords are not used in this tool:") - for key in keys(data["settings"]) - println(" - ",key) - end - end +# TODO: informAboutUnusedKeys(settings, "settings") # inform the user, which Symbols of the input dictionary are not used in this tool return settings -end # function inputSettings +end # function checkAndSetSettings! -""" - getEnum(string, enum_type) +function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol) + 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.") + 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.") + end + return dictionary +end #function checkAndSetBool! -Converts a string to an enumerated type. -But only if the string matches an enumerated value. +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 -# Example -```jldoctest -julia> @enum trainType passenger freight +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.") + 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." ) + end -julia> myTrain = "passenger" -"passenger" + return dictionary +end #function checkAndSetPositiveNumber! -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 +function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String) + 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.") + 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." ) + 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,".") + 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.") + 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." ) + 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]) + + # println("absDiff=", abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train])) + 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,".") + 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.") + 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.") + 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] + + # 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 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 + + 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 + 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.") + end # if + + return train +end #function checkAndSetTractiveEffortVelocityPairs! + +function checkAndSetSections!(path::Dict) + # check the section information + if haskey(path,:sections) && path[:sections]!=nothing + 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 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 ‰) + + 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 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.") + 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 does not euqaul the last position of the previous section. 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 + + return path +end #function checkAndSetSections! + +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) + println(" - ",key) + end + end =# +end #function informAboutUnusedKeys end # module Input diff --git a/src/TrainRun.jl b/src/TrainRun.jl index a4f5fdb..7048af5 100644 --- a/src/TrainRun.jl +++ b/src/TrainRun.jl @@ -1,73 +1,34 @@ module TrainRun -include("./types.jl") -include("./Input.jl") -include("./Preparation.jl") -include("./OperationModes.jl") -include("./Output.jl") +# include main module TrainRunCalc +include("./TrainRunCalc.jl") +#include("./types.jl") +#include("./Input.jl") +#include("./Preparation.jl") +#include("./OperationModes.jl") +#include("./Output.jl") +# include additional modules +include("./Import.jl") -using .types -using .Input -using .Preparation -using .OperationModes -using .Output +# use main module TrainRunCalc +using .TrainRunCalc +#using .types +#using .Input +#using .Preparation +#using .OperationModes +#using .Output -export calculateDrivingDynamics +# use additional modules +using .Import + +# export main function +export calculateDrivingDynamics, + +# export the import functions +importYamlFiles, importYamlFile # approximationLevel = 6 # value for approximation to intersections - # TODO: define it here and give it to each function? (MovingPhases, EnergySaving) - -""" - calculateDrivingDynamics(trainDirectory::String, pathDirectory::String, settingsDirectory::String) - -Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. - -# Examples -```julia-repl -julia> calculateDrivingDynamics(C:\\folder\\train.yaml, C:\\folder\\path.yaml, C:\\folder\\settings.yaml) -todo !!! -``` -""" -function calculateDrivingDynamics(trainDirectory::String, pathDirectory::String, settingsDirectory::String) - # input - (train, path, settings)=readInput(trainDirectory, pathDirectory, settingsDirectory) - println("The input has been saved.") - - - # preparing the input data - movingSection=preparateSections(path, train, settings) - println("The moving section has been prepared.") - - if settings[:operationModeMinimumRunningTime] ==true || settings[:operationModeMinimumEnergyConsumption] ==true - (movingSectionMinimumRunningTime, drivingCourseMinimumRunningTime)=calculateMinimumRunningTime!(movingSection, settings, train) - # println("t=", drivingCourseMinimumRunningTime[end][:t]) - # printSectionInformation(movingSectionMinimumRunningTime) - println("The driving course for the shortest running time has been calculated.") - end #if - - - # oparation mode "minimum energy consumption" - if settings[:operationModeMinimumEnergyConsumption] == true - (movingSectionMinimumEnergyConsumption, drivingCourseMinimumEnergyConsumption)=calculateMinimumEnergyConsumption(movingSectionMinimumRunningTime, drivingCourseMinimumRunningTime, settings, train) - # printSectionInformation(movingSectionMinimumEnergyConsumption) - println("The driving course for the lowest energy consumption has been calculated.") - end #if - - #output - if settings[:operationModeMinimumRunningTime] == true && settings[:operationModeMinimumEnergyConsumption] == true - plotDrivingCourse(drivingCourseMinimumRunningTime, drivingCourseMinimumEnergyConsumption) - return createOutput(settings, path[:name], train[:name], drivingCourseMinimumRunningTime, movingSectionMinimumRunningTime, drivingCourseMinimumEnergyConsumption, movingSectionMinimumEnergyConsumption) - elseif settings[:operationModeMinimumRunningTime] == true - plotDrivingCourse(drivingCourseMinimumRunningTime) - return createOutput(settings, path[:name], train[:name], drivingCourseMinimumRunningTime, movingSectionMinimumRunningTime) - elseif settings[:operationModeMinimumEnergyConsumption] == true - plotDrivingCourse(drivingCourseMinimumEnergyConsumption) - return createOutput(settings, path[:name], train[:name], drivingCourseMinimumEnergyConsumption, movingSectionMinimumEnergyConsumption) - else - println("No Output was demanded. So no output is created.") - return Dict() - end -end # function calculateDrivingDynamics + # TODO: define it here and give it to each function? (MovingPhases, EnergySaving, ..) end # module TrainRun diff --git a/src/TrainRunCalc.jl b/src/TrainRunCalc.jl new file mode 100644 index 0000000..cd41b4f --- /dev/null +++ b/src/TrainRunCalc.jl @@ -0,0 +1,78 @@ +module TrainRunCalc + +# include modules of TrainRunCalc +include("./types.jl") +include("./Input.jl") +include("./Preparation.jl") +include("./OperationModes.jl") +include("./Output.jl") + + +# use modules of TrainRunCalc +using .types +using .Input +using .Preparation +using .OperationModes +using .Output + +# export main function +export calculateDrivingDynamics + +# approximationLevel = 6 # value for approximation to intersections + # TODO: define it here and give it to each function? (MovingPhases, EnergySaving) + +# Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. + +""" + calculateDrivingDynamics(train::Dict, path::Dict, settings::Dict) + +Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding dictionaries `train`, `path`, `settings`. + +# Examples +```julia-repl +julia> calculateDrivingDynamics(trainDict, pathDict, settingsDict) +todo !!! +``` +""" +function calculateDrivingDynamics(train::Dict, path::Dict, settings::Dict) + # check the input data + (train, path, settings) = checkAndSetInput!(train, path, settings) + println("The input has been checked.") + + # prepare the input data + movingSection=preparateSections(path, train, settings) + println("The moving section has been prepared.") + + # calculate the train run for oparation mode "minimum running time" + if settings[:operationModeMinimumRunningTime] ==true || settings[:operationModeMinimumEnergyConsumption] ==true + (movingSectionMinimumRunningTime, drivingCourseMinimumRunningTime)=calculateMinimumRunningTime!(movingSection, settings, train) + # println("t=", drivingCourseMinimumRunningTime[end][:t]) + # printSectionInformation(movingSectionMinimumRunningTime) + println("The driving course for the shortest running time has been calculated.") + end #if + + + # calculate the train run for oparation mode "minimum energy consumption" + if settings[:operationModeMinimumEnergyConsumption] == true + (movingSectionMinimumEnergyConsumption, drivingCourseMinimumEnergyConsumption)=calculateMinimumEnergyConsumption(movingSectionMinimumRunningTime, drivingCourseMinimumRunningTime, settings, train) + # printSectionInformation(movingSectionMinimumEnergyConsumption) + println("The driving course for the lowest energy consumption has been calculated.") + end #if + + #output + if settings[:operationModeMinimumRunningTime] == true && settings[:operationModeMinimumEnergyConsumption] == true + plotDrivingCourse(drivingCourseMinimumRunningTime, drivingCourseMinimumEnergyConsumption) + return createOutput(settings, path[:name], train[:name], drivingCourseMinimumRunningTime, movingSectionMinimumRunningTime, drivingCourseMinimumEnergyConsumption, movingSectionMinimumEnergyConsumption) + elseif settings[:operationModeMinimumRunningTime] == true + plotDrivingCourse(drivingCourseMinimumRunningTime) + return createOutput(settings, path[:name], train[:name], drivingCourseMinimumRunningTime, movingSectionMinimumRunningTime) + elseif settings[:operationModeMinimumEnergyConsumption] == true + plotDrivingCourse(drivingCourseMinimumEnergyConsumption) + return createOutput(settings, path[:name], train[:name], drivingCourseMinimumEnergyConsumption, movingSectionMinimumEnergyConsumption) + else + println("No Output was demanded. So no output is created.") + return Dict() + end +end # function calculateDrivingDynamics + +end # module TrainRunCalc