diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f17d0c..a9b7176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,24 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. ## [Unreleased] +### Added +* dependency JSONSchema +* validation of YAML input via JSON schema + +### Changed +* replaced settings::Dict with type Settings as struct +* restructured examples/ and data/ in docs/ and test/ +* modified test to work with Julia Testsets and with simplier naming of input files +* renamed Validate.jl into types.jl +* renamed TrainRunCalc.jl into calc.jl +* changed capital letter of include files to lower letter +* changed seperation of submodules into a single module with file include + +### Removed +* dependency Plots +* AdditionalOutput.jl +* EnergySaving.jl +* test/testEnums.jl ## Version [0.8] 2022-01-20 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4743fbf..fa259f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,15 @@ email, or any other method with the owners of this repository before making a ch Please note we have a code of conduct, please follow it in all your interactions with the project. +# Julia Development Environment + +``` +$ ln -s ~/path/to/TrainRun.jl ~/.julia/dev/TrainRun +``` + +and use `Revise.jl` + + # Pull Request Process * add your changes to the CHANGELOG.md under [Unreleased] diff --git a/Project.toml b/Project.toml index c387903..a989380 100644 --- a/Project.toml +++ b/Project.toml @@ -1,12 +1,20 @@ name = "TrainRun" uuid = "e4541106-d44c-4e00-b50b-ecdf479fcf92" -authors = ["Max Kannenberg"] +authors = ["Max Kannenberg, Martin Scheidt, and contributors"] version = "0.8.0" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" + +[compat] +julia = "1.7" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md index 88d15dc..75e0a3a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # TrainRun -[![License: ISC](https://img.shields.io/badge/license-ISC-green.svg)](https://opensource.org/licenses/ISC) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6448563.svg)](https://doi.org/10.5281/zenodo.6448563) +[![License: ISC](https://img.shields.io/badge/license-ISC-green.svg)](https://opensource.org/licenses/ISC) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6448563.svg)](https://doi.org/10.5281/zenodo.6448563) +[![Build Status](https://github.com/railtoolkit/TrainRun.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/railtoolkit/TrainRun.jl/actions/workflows/CI.yml?query=branch%3Amaster) ------------ diff --git a/data/settings/settings_distanceStep_homStrip.yaml b/data/settings/settings_distanceStep_homStrip.yaml deleted file mode 100644 index 2c8738c..0000000 --- a/data/settings/settings_distanceStep_homStrip.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "homogeneous strip" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "s in m" # step variable of the step method "s in m", "t in s" or "v in m/s" - 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: "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/settings/settings_distanceStep_massPoint.yaml b/data/settings/settings_distanceStep_massPoint.yaml deleted file mode 100644 index fc616ed..0000000 --- a/data/settings/settings_distanceStep_massPoint.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "mass point" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "s in m" # step variable of the step method "s in m", "t in s" or "v in m/s" - 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: "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/settings/settings_distanceStep_massPoint_runningTime.yaml b/data/settings/settings_distanceStep_massPoint_runningTime.yaml deleted file mode 100644 index 98d7e84..0000000 --- a/data/settings/settings_distanceStep_massPoint_runningTime.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "mass point" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "s in m" # step variable of the step method "s in m", "t in s" or "v in m/s" - 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: "running time" # 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/settings/settings_timeStep_homStrip.yaml b/data/settings/settings_timeStep_homStrip.yaml deleted file mode 100644 index 952db02..0000000 --- a/data/settings/settings_timeStep_homStrip.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "homogeneous strip" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "t in s" # step variable of the step method "s in m", "t in s" or "v in m/s" - stepSize: 3.0 # 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: "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/settings/settings_timeStep_massPoint.yaml b/data/settings/settings_timeStep_massPoint.yaml deleted file mode 100644 index 6e2eae0..0000000 --- a/data/settings/settings_timeStep_massPoint.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "mass point" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "t in s" # step variable of the step method "s in m", "t in s" or "v in m/s" - stepSize: 3.0 # 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: "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/settings/settings_velocityStep_massPoint.yaml b/data/settings/settings_velocityStep_massPoint.yaml deleted file mode 100644 index 54327a2..0000000 --- a/data/settings/settings_velocityStep_massPoint.yaml +++ /dev/null @@ -1,12 +0,0 @@ -%YAML 1.2 ---- -settings: -# settings for the simulation - massModel: "mass point" # model type of train mass "mass point" or "homogeneous strip" - stepVariable: "v in m/s" # step variable of the step method "s in m", "t in s" or "v in m/s" - stepSize: 0.1 # 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: "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/docs/DEFAULT.yaml b/docs/DEFAULT.yaml new file mode 100644 index 0000000..1a81cff --- /dev/null +++ b/docs/DEFAULT.yaml @@ -0,0 +1,11 @@ +%YAML 1.2 +--- +settings: +# default settings for the calculation + massModel: "mass_point" # type of train model used: "mass_point" or "homogeneous_strip" + stepVariable: "distance" # variable of the linear multistep method: "distance", "time" or "velocity" + stepSize: 20 # step size, unit depends on stepVariable - distance in meter, time in seconds and velocity in meter/second. + approxLevel: 3 # value for approximation; used when rounding or interating + outputDetail: "running_time" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything" + outputFormat: "julia_dict" # output as "julia_dict" or as "csv" + outputDir: "." # used if other outputFormat than "julia dict" diff --git a/docs/examples/ExtendedWorkingExample.jl b/docs/examples/ExtendedWorkingExample.jl new file mode 100644 index 0000000..e3c8c58 --- /dev/null +++ b/docs/examples/ExtendedWorkingExample.jl @@ -0,0 +1,32 @@ +#!/usr/bin/env julia + +import TrainRun + +paths=[] +push!(paths, importFromYaml(:path, "data/paths/path_1_10km_nConst_vConst.yaml")) +push!(paths, importFromYaml(:path, "data/paths/path_2_10km_nVar_vConst.yaml")) +push!(paths, importFromYaml(:path, "data/paths/path_3_10km_nConst_vVar.yaml")) +push!(paths, importFromYaml(:path, "data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml")) + +settings=[] +push!(settings, importFromYaml(:settings, "data/settings/settings_distanceStep_massPoint.yaml")) + +trains=[] +push!(trains, importFromYaml(:train, "data/trains/train_freight_V90withOreConsist.yaml")) +push!(trains, importFromYaml(:train, "data/trains/train_passenger_SiemensDesiroClassic.yaml")) +push!(trains, importFromYaml(:train, "data/trains/train_passenger_IC2.yaml")) + +for path in paths + # println(" - - - - - - - - -") + # println("path: ", path[:name]) + for train in trains + # println("train: ", train[:name]) + for settings in settings + resultsDict = trainRun(train, path, settings) + if haskey(settings, :outputFormat) && settings[:outputFormat] == "CSV" + exportToCsv(resultsDict, settings) + sleep(2) + end + end + end +end diff --git a/docs/examples/MinimalWorkingExample.jl b/docs/examples/MinimalWorkingExample.jl new file mode 100644 index 0000000..581421e --- /dev/null +++ b/docs/examples/MinimalWorkingExample.jl @@ -0,0 +1,10 @@ +#!/usr/bin/env julia + +import TrainRun + +train = Train("data/trains/train_freight_V90withOreConsist.yaml") +path = Path("data/paths/path_1_10km_nConst_vConst.yaml") + +runtime = trainRun(train, path) + +println("The train needs $runtime seconds for the running path.") diff --git a/examples/ExtendedWorkingExample.jl b/examples/ExtendedWorkingExample.jl deleted file mode 100644 index ebb68e3..0000000 --- a/examples/ExtendedWorkingExample.jl +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.0 -# __author__ = "Max Kannenberg" -# __copyright__ = "2021" -# __license__ = "ISC" - -include("../src/TrainRun.jl") -using .TrainRun - -allPaths=[] -push!(allPaths, importFromYaml(:path, "data/paths/path_1_10km_nConst_vConst.yaml")) -push!(allPaths, importFromYaml(:path, "data/paths/path_2_10km_nVar_vConst.yaml")) -push!(allPaths, importFromYaml(:path, "data/paths/path_3_10km_nConst_vVar.yaml")) -push!(allPaths, importFromYaml(:path, "data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml")) - -allSettings=[] -push!(allSettings, importFromYaml(:settings, "data/settings/settings_distanceStep_massPoint.yaml")) - -allTrains=[] -push!(allTrains, importFromYaml(:train, "data/trains/train_freight_V90withOreConsist.yaml")) -push!(allTrains, importFromYaml(:train, "data/trains/train_passenger_SiemensDesiroClassic.yaml")) -push!(allTrains, importFromYaml(:train, "data/trains/train_passenger_IC2.yaml")) - -for path in allPaths - # println(" - - - - - - - - -") - # println("path: ", path[:name]) - for train in allTrains - # println("train: ", train[:name]) - for settings in allSettings - resultsDict = trainRun(train, path, settings) - if haskey(settings, :typeOfOutput) && settings[:typeOfOutput] == "CSV" - exportToCsv(resultsDict, settings) - sleep(2) - end - # println("") - end - end - # println("") -end - -# println("") -# println("________________________") -# println("") diff --git a/examples/MinimalWorkingExample.jl b/examples/MinimalWorkingExample.jl deleted file mode 100644 index 47382f5..0000000 --- a/examples/MinimalWorkingExample.jl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.0 -# __author__ = "Max Kannenberg" -# __copyright__ = "2021" -# __license__ = "ISC" - -include("../src/TrainRun.jl") -using .TrainRun - -train_directory = "data/trains/train_freight_V90withOreConsist.yaml" -running_path_directory = "data/paths/path_1_10km_nConst_vConst.yaml" -setting_directory = "data/settings/settings_distanceStep_massPoint_runningTime.yaml" -(train, running_path, settings) = importYamlFiles(train_directory, running_path_directory, setting_directory) - -runtime = trainRun(train, running_path, settings) - -exportToCsv(runtime, settings) -println("The V 90 with 10 ore wagons needs $runtime seconds for 10 km with no gradient.") diff --git a/src/AdditionalOutput.jl b/src/AdditionalOutput.jl deleted file mode 100644 index c7079ba..0000000 --- a/src/AdditionalOutput.jl +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -# INFO: AdditionalOutput should not be used because it is not completed yet. It was used to show first results during development. -# TODO: It has to be optimized so that the created plots and printed information is clear and understandable. - -module AdditionalOutput - -using Plots - -export plotResults, plotDrivingCourse, printImportantValues, printSectionInformation - -function plotResults(output::Dict) - opModeMinTime = output[:settings][:operationModeMinimumRunningTime] - opModeMinEnergy = output[:settings][:operationModeMinimumEnergyConsumption] - - if opModeMinTime == true && opModeMinEnergy == true - plotDrivingCourse(output[:drivingCourseMinimumRunningTime], output[:drivingCourseMinimumEnergyConsumption]) - elseif opModeMinTime == true - plotDrivingCourse(output[:drivingCourseMinimumRunningTime]) - elseif opModeMinEnergy == true - plotDrivingCourse(output[:drivingCourseMinimumEnergyConsumption]) - else - output[:settings][:detailOfOutput] == "everything" && println("No Output was demanded. So no plot is created.") - end - - return true -end #function plotResults - -function plotResults(drivingCourse::Vector{Dict}) - plotDrivingCourse(drivingCourse) - - return true -end #function plotResults - -function plotResults(singleValue::AbstractFloat) - println("Not able to plot the single value ",singleValue) - - return false -end #function plotResults - -function plotDrivingCourse(drivingCourse::Vector{Dict}) - a=[] - E=[] - s=[] - t=[] - v=[] - for i in 1:length(drivingCourse) - push!(a, drivingCourse[i][:a]) - push!(E, drivingCourse[i][:E]) - push!(s, drivingCourse[i][:s]) - push!(t, drivingCourse[i][:t]) - push!(v, drivingCourse[i][:v]) - end #for - -# p1=plot([s], [v], title = "v in m/s", label = ["v"], xlabel = "s in m") - p1=plot([s/1000], [v*3.6], title = "v in km/h", label = ["v"], xlabel = "s in km") - -# p2=plot([t], [v], title = "v in m/s", label = ["v"], xlabel = "t in s") - p2=plot([t/60], [v*3.6], title = "v in km/h", label = ["v"], xlabel = "t in min") - - # p3=plot([s], [t], title = "t in s", label = ["t"], xlabel = "s in m") - - # p4=plot([t], [s], title = "s in m", label = ["s"], xlabel = "t in s") - - #p5=plot([s], [E], title = "E in Ws", label = ["E"], xlabel = "s in m") - p5=plot([s/1000], [E], title = "E in Ws", label = ["E"], xlabel = "s in km") - - #p6=plot([t], [E], title = "E in Ws", label = ["E"], xlabel = "t in s") - p6=plot([t/60], [E], title = "E in Ws", label = ["E"], xlabel = "t in min") - - all=plot(p1, p2, p5, p6, layout = (2, 2), legend = false) -#= - # p5=plot([s], [E], title = "E in Ws", label = ["E"], xlabel = "s in m") - - # p6=plot([t], [E], title = "E in Ws", label = ["E"], xlabel = "t in s") - - all=plot(p1, p2, layout = (1, 2), legend = false)=# - # all=plot(p1, p2, p3, p4, p5, p6, layout = (3, 2), legend = false) - display(all) - println("Plots for different variables have been created.") -end #function plotDrivingCourse - -function plotDrivingCourse(drivingCourseMinimumRunningTime::Vector{Dict},drivingCourseMinimumEnergyConsumption::Vector{Dict}) - a_minTime=[] - E_minTime=[] - s_minTime=[] - t_minTime=[] - v_minTime=[] - for i in 1:length(drivingCourseMinimumRunningTime) - push!(a_minTime, drivingCourseMinimumRunningTime[i][:a]) - push!(E_minTime, drivingCourseMinimumRunningTime[i][:E]) - push!(s_minTime, drivingCourseMinimumRunningTime[i][:s]) - push!(t_minTime, drivingCourseMinimumRunningTime[i][:t]) - push!(v_minTime, drivingCourseMinimumRunningTime[i][:v]) - end #for - - a_minEnergy=[] - E_minEnergy=[] - s_minEnergy=[] - t_minEnergy=[] - v_minEnergy=[] - for i in 1:length(drivingCourseMinimumEnergyConsumption) - push!(a_minEnergy, drivingCourseMinimumEnergyConsumption[i][:a]) - push!(E_minEnergy, drivingCourseMinimumEnergyConsumption[i][:E]) - push!(s_minEnergy, drivingCourseMinimumEnergyConsumption[i][:s]) - push!(t_minEnergy, drivingCourseMinimumEnergyConsumption[i][:t]) - push!(v_minEnergy, drivingCourseMinimumEnergyConsumption[i][:v]) - end #for - - p1=plot([s_minTime,s_minEnergy], - [v_minTime,v_minEnergy], - title = "v in m/s", - label = ["v for t_min" "v for E_min"], - xlabel = "s in m")# lw = 3) - - p2=plot([t_minTime,t_minEnergy], - [v_minTime,v_minEnergy], - title = "v in m/s", - label = ["v for t_min" "v for E_min"], - xlabel = "t in s") - - # p3=plot([s_minTime,s_minEnergy], - # [t_minTime,t_minEnergy], - # title = "t in s", - # label = ["t for t_min" "t for E_min"], - # xlabel = "s in m") - - # p4=plot([t_minTime,t_minEnergy], - # [s_minTime,s_minEnergy], - # title = "s in m", - # label = ["s for t_min" "s for E_min"], - # xlabel = "t in s") - - p5=plot([s_minTime,s_minEnergy], - [E_minTime,E_minEnergy], - title = "E in Ws", - label = ["E for t_min" "E for E_min"], - xlabel = "s in m") - - p6=plot([t_minTime,t_minEnergy], - [E_minTime,E_minEnergy], - title = "E in Ws", - label = ["E for t_min" "E for E_min"], - xlabel = "t in s") - - # all=plot(p1, p2, p3, p4, p5, p6, layout = (3, 2), legend = false) - all=plot(p1, p2, p5, p6, layout = (2, 2), legend = false) - display(all) - println("Plots for different variables have been created.") -end #function plotDrivingCourse - -function printImportantValues(dataPoints::Vector{Dict}) - println("i behavior s in m v in km/h t in min a in m/s^2 F_R in k N F_T in k N E in k Wh") - for i in 1:length(dataPoints) - println(dataPoints[i][:i],". ",dataPoints[i][:behavior]," ",dataPoints[i][:s]," ",dataPoints[i][:v]*3.6," ",dataPoints[i][:t]/60," ",dataPoints[i][:a]," ",dataPoints[i][:F_R]/1000," ",dataPoints[i][:F_T]/1000," ",dataPoints[i][:E]/3600/1000) - end #for - println("i behavior s in m v in km/h t in min a in m/s^2 F_R in k N F_T in k N E in k Wh") -end #function printImportantValues - -function printSectionInformation(movingSection::Dict) - CSs::Vector{Dict} = movingSection[:characteristicSections] - - println("MS with length=", movingSection[:length]," with t=", movingSection[:t]) - #allBSs=[:breakFree, :clearing, :accelerating, :clearing2, :accelerating2, :clearing3, :accelerating3, :cruising, :downhillBraking, :diminishing, :coasting, :braking, :standstill] - for csId in 1:length(CSs) - println("CS ",csId," with length=", CSs[csId][:length]," with t=", CSs[csId][:t]) - # for bs in 1: length(allBSs) - # if haskey(CSs[csId][:behaviorSections], allBSs[bs]) - # println("BS ",allBSs[bs], " with s_entry=", CSs[csId][:behaviorSections][allBSs[bs]][:s_entry], " and length=",CSs[csId][:behaviorSections][allBSs[bs]][:length]) # and t=", CSs[csId][:behaviorSections][allBSs[bs]][:t]) - # # for point in 1:length(CSs[csId][:behaviorSections][allBSs[bs]][:dataPoints]) - # # println(CSs[csId][:behaviorSections][allBSs[bs]][:dataPoints][point]) - # # end - # end #if - # end #for - - tempBSs = collect(keys(CSs[csId][:behaviorSections])) - while length(tempBSs) > 0 - currentBS = :default - for bs in 1: length(tempBSs) - if currentBS == :default - currentBS = tempBSs[bs] - else - if CSs[csId][:behaviorSections][currentBS][:s_entry] > CSs[csId][:behaviorSections][tempBSs[bs]][:s_entry] - currentBS = tempBSs[bs] - end - end - end #for - - println("BS ",currentBS, " with s_entry=", CSs[csId][:behaviorSections][currentBS][:s_entry], " and length=",CSs[csId][:behaviorSections][currentBS][:length]) # and t=", CSs[csId][:behaviorSections][currentBS][:t]) - # for point in 1:length(CSs[csId][:behaviorSections][currentBS][:dataPoints]) - # println(CSs[csId][:behaviorSections][currentBS][:dataPoints][point]) - # end - - newTempBSs = [] - for bs in 1: length(tempBSs) - if currentBS != tempBSs[bs] - push!(newTempBSs, tempBSs[bs]) - end #if - end #for - tempBSs = newTempBSs - end #while - end #for -end #function printSectionInformation - -end # module AdditionalOutput diff --git a/src/Behavior.jl b/src/Behavior.jl index 8d65c09..d1815d2 100644 --- a/src/Behavior.jl +++ b/src/Behavior.jl @@ -5,22 +5,6 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module Behavior - -include("./Formulary.jl") -using .Formulary - -export addBreakFreeSection!, addClearingSection!, addAcceleratingSection!, addCruisingSection!, addDiminishingSection!, addCoastingSection!, addBrakingSection!, addStandstill!, -# addBrakingSectionInOneStep! is not used in the current version of the tool -calculateForces!, createDataPoint, - -# export functions from Formulary -calcBrakingDistance, calcBrakingStartVelocity, calc_Δs_with_Δt - - -approximationLevel = 6 # value for approximation to intersections TODO further explanation (e.g. approximationLevel = 3 -> with stepSize 10 m the approximation will be calculated accurate on 10 mm ; 1s -> 1 ms; 1 km/h -> 3.6 mm/s) - # TODO: define it in TrainRun and give it to each function? - ## functions for calculating tractive effort and resisting forces """ calculateTractiveEffort(v, tractiveEffortVelocityPairs) @@ -62,10 +46,11 @@ end #function calculateTractiveEffort """ calculate and return the path resistance dependend on the trains position and mass model """ -function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel::String, train::Dict) - if massModel == "mass point" +function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) + + if massModel == :mass_point pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) - elseif massModel == "homogeneous strip" + elseif massModel == :homogeneous_strip pathResistance = 0.0 s_rear = s - train[:length] # position of the rear of the train while csId > 0 && s_rear < CSs[csId][:s_exit] @@ -84,7 +69,7 @@ end #function calculatePathResistance """ calculate and return tractive and resisting forces for a data point """ -function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Dict, massModel::String) +function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Dict, massModel) # calculate resisting forces dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) @@ -108,7 +93,7 @@ end #function calculateForces! """ TODO """ -function moveAStep(previousPoint::Dict, stepVariable::String, stepSize::Real, csId::Integer) +function moveAStep(previousPoint::Dict, stepVariable::Symbol, stepSize::Real, csId::Integer) # stepSize is the currentStepSize depending on the accessing function # TODO: csId is only for error messages. Should it be removed? #= 08/31 TODO: How to check if the train stopps during this step? I should throw an error myself that I catch in higher hierarchies. =# @@ -118,7 +103,7 @@ function moveAStep(previousPoint::Dict, stepVariable::String, stepSize::Real, cs newPoint[:i] = previousPoint[:i]+1 # identifier # calculate s, t, v, E - if stepVariable == "s in m" # distance step method + if stepVariable == :distance # distance step method newPoint[:Δs] = stepSize # step size (in m) if previousPoint[:a] == 0.0 if previousPoint[:v] == 0.0 @@ -138,12 +123,12 @@ function moveAStep(previousPoint::Dict, stepVariable::String, stepSize::Real, cs newPoint[:Δv] = calc_Δv_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in m/s) end - elseif stepVariable == "t in s" # time step method + elseif stepVariable == :time # time step method newPoint[:Δt] = stepSize # step size (in s) newPoint[:Δs] = calc_Δs_with_Δt(newPoint[:Δt], previousPoint[:a], previousPoint[:v]) # step size (in m) newPoint[:Δv] = calc_Δv_with_Δt(newPoint[:Δt], previousPoint[:a]) # step size (in m/s) - elseif stepVariable == "v in m/s" # velocity step method + elseif stepVariable == :velocity # velocity step method if previousPoint[:a] == 0.0 if previousPoint[:v] == 0.0 error("ERROR: The train tries to cruise at v=0.0 m/s at s=",previousPoint[:s]," in CS",csId,".") @@ -203,7 +188,7 @@ end #function getNextPointOfInterest ## This function calculates the data points of the breakFree section. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. # Info: currently the values of the breakFree section will be calculated like in the accelerating section -function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Dict, train::Dict, CSs::Vector{Dict}) +function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) # conditions for the break free section endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] trainIsHalting = drivingCourse[end][:v] == 0.0 @@ -213,7 +198,7 @@ function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags: drivingCourse[end][:behavior] = BS[:type] # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings[:massModel]) # currently the tractive effort is calculated like in the accelerating section + calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) # currently the tractive effort is calculated like in the accelerating section # calculate the breakFree section with calculating the accelerating section and just using the first step and removing the rest try (CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) @@ -272,7 +257,7 @@ end #function addBreakFreeSection! ## This function calculates the data points of the clearing section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. -function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Dict, train::Dict, CSs::Vector{Dict}) +function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) if stateFlags[:previousSpeedLimitReached] currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) @@ -287,7 +272,7 @@ function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: s_clearing = min(CS[:s_exit]-drivingCourse[end][:s]-s_braking, currentSpeedLimit[:s_end] - drivingCourse[end][:s]) if s_clearing > 0.0 (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_clearing, settings, train, CSs, "clearing") - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) # stateFlags[:brakingStartReached] = brakingStartReached # stateFlags[:endOfCSReached] = stateFlags[:endOfCSReached] || drivingCourse[end][:s] == CS[:s_exit] else @@ -305,14 +290,14 @@ end #function addClearingSection ## This function calculates the data points of the accelerating section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section -function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Dict, train::Dict, CSs::Vector{Dict}) - #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) - #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Dict, train::Dict) +function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) + #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) CSs = movingSection[:characteristicSections] CS = CSs[csId] drivingCourse = movingSection[:drivingCourse]=# - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] ignoreBraking = true @@ -340,11 +325,11 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla #speedLimitReached = drivingCourse[end][:v] > currentSpeedLimit[:v] #targetSpeedReached = speedLimitReached while !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached && !previousSpeedLimitReached - currentStepSize = settings[:stepSize] # initialize the step size that can be reduced near intersections + currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation + for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation if !ignoreBraking s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) end @@ -360,11 +345,11 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # conditions for the next while cycle if !ignoreBraking @@ -386,41 +371,41 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla end # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < approximationLevel+1 + if cycle < settings.approxLevel+1 if drivingCourse[end][:F_T] <= drivingCourse[end][:F_R] testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," <= F_R=",drivingCourse[end][:F_R]) # for testing - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif s_braking > 0.0 && drivingCourse[end][:s] + s_braking > CS[:s_exit] testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif drivingCourse[end][:s] > nextPointOfInterest testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:v] > CS[:v_peak] testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=",CS[:v_peak]) # for testing - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == :speed currentStepSize = CS[:v_peak]-drivingCourse[end-1][:v] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:v] > currentSpeedLimit[:v] testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_limitCurrent=",currentSpeedLimit[:v]) # for testing - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == :velocity currentStepSize = currentSpeedLimit[:v]-drivingCourse[end-1][:v] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end - elseif drivingCourse[end][:s] +s_braking == CS[:s_exit] + elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing if s_braking == 0.0 endOfCSReached = true @@ -447,7 +432,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest) println("F_T=",drivingCourse[end][:F_T] ," F_R=", drivingCourse[end][:F_R]) - error("ERROR at accelerating section: With the step variable ",settings[:stepVariable]," the while loop will be left although v 1 + if settings.massModel == :homogeneous_strip && CS[:id] > 1 # conditions for cruising section trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising @@ -604,11 +589,11 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: #&& targetSpeedReached # use the conditions for the cruising section while trainInPreviousCS && !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - currentStepSize = settings[:stepSize] + currentStepSize = settings.stepSize nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation + for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] # the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS @@ -626,21 +611,21 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: drivingCourse[end][:a] = 0.0 # create the next data point - if settings[:stepVariable] =="s in m" || settings[:stepVariable] =="t in s" - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + if settings.stepVariable == :distance || settings.stepVariable == time + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) else - push!(drivingCourse, moveAStep(drivingCourse[end], "s in m", train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? + push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? end drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings[:massModel]) -# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) +# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings[:massModel]) + # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings[:massModel]) + # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) #end # conditions for the next while cycle @@ -652,28 +637,28 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: end #while # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < approximationLevel+1 + if cycle < settings.approxLevel+1 if drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif !trainIsBrakingDownhill && resistingForceNegative - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif trainIsBrakingDownhill && !resistingForceNegative - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif drivingCourse[end][:s] > nextPointOfInterest - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train[:length])) - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] @@ -689,7 +674,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: break else - error("ERROR at cruising section: With the step variable ",settings[:stepVariable]," the while loop will be left although the if cases don't apply in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") + error("ERROR at cruising section: With the step variable ",settings.stepVariable," the while loop will be left although the if cases don't apply in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") end # delete last data point for recalculating the last step with reduced step size @@ -757,16 +742,16 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: s_cruisingRemaining = min(nextPointOfInterest -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s]) # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], "s in m", s_cruisingRemaining, CS[:id])) + push!(drivingCourse, moveAStep(drivingCourse[end], :distance, s_cruisingRemaining, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings[:massModel]) -# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) +# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings[:massModel]) + # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings[:massModel]) + # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) #end # conditions for the next while cycle @@ -808,8 +793,8 @@ end #function addCruisingSection! ## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower -function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Dict, train::Dict, CSs::Vector{Dict}) - calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings[:massModel]) +function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] ignoreBraking = true @@ -832,22 +817,22 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag drivingCourse[end][:behavior] = BS[:type] while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached - currentStepSize=settings[:stepSize] # initialize the step size that can be reduced near intersections + currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation + for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached # 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end # acceleration (in m/s^2): drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # conditions for the next while cycle if !ignoreBraking @@ -867,27 +852,27 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag end # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < approximationLevel+1 + if cycle < settings.approxLevel+1 if drivingCourse[end][:v] < 0.0 - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == velocity currentStepSize = drivingCourse[end-1][:v] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," > F_R=",drivingCourse[end][:F_R]) # for testing - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif s_braking > 0.0 && drivingCourse[end][:s] + s_braking > CS[:s_exit] testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif drivingCourse[end][:s] > nextPointOfInterest testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] @@ -908,7 +893,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag " F_T=",drivingCourse[end-1][:F_T]," N R_traction=",drivingCourse[end-1][:R_traction]," N R_wagons=",drivingCourse[end-1][:R_wagons]," N R_path=",drivingCourse[end-1][:R_path]," N.") else - error("ERROR during diminishing run: With the step variable ",settings[:stepVariable]," the while loop will be left although s+s_braking0.0 in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") + error("ERROR during diminishing run: With the step variable ",settings.stepVariable," the while loop will be left although s+s_braking0.0 in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") end # delete last data point for recalculating the last step with reduced step size pop!(drivingCourse) @@ -995,7 +980,7 @@ end #function addDiminishingSection! ## This function calculates the data points of the coasting section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section -function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Dict, train::Dict, CSs::Vector{Dict}) +function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) # TODO: if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept # with getCurrentSpeedLimit @@ -1012,21 +997,21 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: drivingCourse[end][:behavior] = BS[:type] while !targetSpeedReached && !endOfCSReached && !brakingStartReached - currentStepSize=settings[:stepSize] # initialize the step size that can be reduced near intersections + currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation + for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation while !targetSpeedReached && !brakingStartReached && !pointOfInterestReached # 03/09 old : while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:v] <= CS[:v_peak] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest # traction effort and resisting forces (in N): - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # acceleration (in m/s^2): drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) @@ -1040,33 +1025,33 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: testFlag = false # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < approximationLevel+1 + if cycle < settings.approxLevel+1 if drivingCourse[end][:s] + s_braking > CS[:s_exit] testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle elseif drivingCourse[end][:s] > nextPointOfInterest testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:v] < CS[:v_exit] # TODO: if accelereation and coasting functions will be combined this case is only for coasting testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," < v_exit=", CS[:v_exit]) # for testing - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == velocity currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:v] > CS[:v_peak] testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=", CS[:v_peak]) # for testing - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == velocity currentStepSize = CS[:v_peak] - drivingCourse[end-1][:v] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing @@ -1082,7 +1067,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: else # TODO: not needed. just for testing - error("ERROR at coasting until braking section: With the step variable ",settings[:stepVariable]," the while loop will be left although v= CS[:s_exit] || stateFlags[:endOfCSReached] @@ -1169,21 +1154,21 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D drivingCourse[end][:behavior] = BS[:type] while !targetSpeedReached && !endOfCSReached - currentStepSize = settings[:stepSize] # initialize the step size that can be reduced near intersections + currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - for cycle in 1:approximationLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation + for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation while !targetSpeedReached && !endOfCSReached && !pointOfInterestReached # 03/09 old: while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest # traction effort and resisting forces (in N): - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # acceleration (in m/s^2): drivingCourse[end][:a] = train[:a_braking] # TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s]) - if settings[:stepVariable] == "s in m" && ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 + if settings.stepVariable == :distance && ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 # create empty data point and set it for the values of s_exit and v_exit push!(drivingCourse, createDataPoint()) drivingCourse[end][:i] = drivingCourse[end-1][:i]+1 @@ -1192,7 +1177,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) else # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings[:stepVariable], currentStepSize, CS[:id])) + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) end @@ -1206,18 +1191,18 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D # check which limit was reached and adjust the currentStepSize for the next cycle # TODO: is there a better way than rounding like in the following? - if cycle < approximationLevel+1 + if cycle < settings.approxLevel+1 if drivingCourse[end][:v] < CS[:v_exit] - if settings[:stepVariable] == "v in m/s" + if settings.stepVariable == velocity currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:s] > nextPointOfInterest - if settings[:stepVariable] == "s in m" + if settings.stepVariable == :distance currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] else - currentStepSize = settings[:stepSize] / 10.0^cycle + currentStepSize = settings.stepSize / 10.0^cycle end elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] break @@ -1306,7 +1291,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] stateFlags[:endOfCSReached] = endOfCSReached stateFlags[:error] = !(endOfCSReached) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 return (CS, drivingCourse, stateFlags) @@ -1315,7 +1300,7 @@ end #function addBrakingSection! ## This function calculates the data point of the standstill. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. -function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, CSs::Vector{Dict}) +function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) if drivingCourse[end][:v] == 0.0 BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) merge!(BS, Dict(:length => 0.0, # total length (in m) @@ -1326,7 +1311,7 @@ function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Dict, t drivingCourse[end][:behavior] = BS[:type] # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings[:massModel]) + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) merge!(CS[:behaviorSections], Dict(:standstill => BS)) end # else: return the characteristic section without a standstillSection section @@ -1411,5 +1396,3 @@ function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) currentPoint[:ΔE] = currentPoint[:ΔW] # energy consumption in this step (in Ws) currentPoint[:E] = previousPoint[:E] + currentPoint[:ΔE] # energy consumption (in Ws) end #function recalculateLastBrakingPoint - -end #module Behavior diff --git a/src/TrainRunCalc.jl b/src/Calc.jl similarity index 75% rename from src/TrainRunCalc.jl rename to src/Calc.jl index e41d5c8..3ba2662 100644 --- a/src/TrainRunCalc.jl +++ b/src/Calc.jl @@ -5,83 +5,55 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module TrainRunCalc - -# include modules of TrainRunCalc -include("./Validate.jl") -include("./Characteristics.jl") -include("./Behavior.jl") -include("./Output.jl") - - -# use modules of TrainRunCalc -using .Validate -using .Characteristics -using .Behavior -using .Output - -# export main function -export trainRun - -approximationLevel = 6 # value for approximation to intersections and precisely calculated digits - # TODO: define it here and give it to each function? (Behavior, ...) - # Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. """ - trainRun(train::Dict, path::Dict, settings::Dict) + trainRun(train::Dict, path::Dict, settings::Settings) Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding dictionaries `train`, `path`, `settings`. # Examples ```julia-repl -julia> trainRun(trainDict, pathDict, settingsDict) +julia> trainRun(trainDict, pathDict) todo !!! ``` """ -function trainRun(trainInput::Dict, pathInput::Dict, settingsInput::Dict) +function trainRun(trainInput::Dict, pathInput::Dict, settings=Settings()::Settings) # copy Input data for not changing them - # TODO: or should they be changed? normally it would only make it "better" except for settings[:detailOfOutput] == "points of interest" && !haskey(path, :pointsOfInterest) + # TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) train = copy(trainInput) path = copy(pathInput) - settings = copy(settingsInput) # check the input data - (train, path, settings) = checkAndSetInput!(train, path, settings) - settings[:detailOfOutput] == "everything" && println("The input has been checked.") + (train, path) = checkAndSetInput!(train, path, settings) + settings.outputDetail == :everything && println("The input has been checked.") # prepare the input data movingSection = determineCharacteristics(path, train, settings) - settings[:detailOfOutput] == "everything" && println("The moving section has been prepared.") + settings.outputDetail == :everything && println("The moving section has been prepared.") # calculate the train run for oparation mode "minimum running time" - if settings[:operationModeMinimumRunningTime] || settings[:operationModeMinimumEnergyConsumption] - (movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train) - settings[:detailOfOutput] == "everything" && println("The driving course for the shortest running time has been calculated.") + (movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train) + settings.outputDetail == :everything && println("The driving course for the shortest running time has been calculated.") - # accumulate data and create an output dictionary - output = createOutput(train, settings, path, movingSection, drivingCourse) - # 30/31 old: output = createOutputDict(train, settings, path, movingSection, drivingCourse) - else - output = nothing - # 30/31 old: output = Dict() - end #if + # accumulate data and create an output dictionary + output = createOutput(train, settings, path, movingSection, drivingCourse) return output end # function trainRun # calculate a train run focussing on using the minimum possible running time -function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train::Dict) +function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) CSs::Vector{Dict} = movingSection[:characteristicSections] - if settings[:massModel] == "homogeneous strip" && settings[:stepVariable] == "v in m/s" + if settings.massModel == :homogeneous_strip && settings.stepVariable == speed println("WARNING: ! ! ! TrainRun.jl doesn't work reliably for the mass model homogeneous strip with step size v in m/s. The calculation time can be extremely high when calcutlating paths with steep gradients ! ! !") end startingPoint=createDataPoint() startingPoint[:i]=1 startingPoint[:s]=CSs[1][:s_entry] - calculateForces!(startingPoint, CSs, 1, "default", train, settings[:massModel]) # traction effort and resisting forces (in N) + calculateForces!(startingPoint, CSs, 1, "default", train, settings.massModel) # traction effort and resisting forces (in N) drivingCourse::Vector{Dict} = [startingPoint] # List of data points for csId in 1:length(CSs) @@ -96,7 +68,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train # determine the different flags for switching between the states for creatinge moving phases s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings[:massModel]) # tractive effort and resisting forces (in N) + calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N) previousSpeedLimitReached = false stateFlags = Dict(:endOfCSReached => drivingCourse[end][:s] > CS[:s_exit], @@ -122,11 +94,11 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] # cruise only one step - if settings[:stepVariable] =="s in m" - s_cruising = settings[:stepSize] - elseif settings[:stepVariable] =="t in s" - s_cruising = calc_Δs_with_Δt(settings[:stepSize], drivingCourse[end][:a], drivingCourse[end][:v]) - elseif settings[:stepVariable] =="v in m/s" + if settings.stepVariable == :distance + s_cruising = settings.stepSize + elseif settings.stepVariable == time + s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v]) + elseif settings.stepVariable == velocity s_cruising = train[:length]/(10.0) # TODO which step size should be used? end (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") @@ -186,5 +158,3 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Dict, train return (movingSection, drivingCourse) end #function calculateMinimumRunningTime - -end # module TrainRunCalc diff --git a/src/Characteristics.jl b/src/Characteristics.jl index 023e79f..4dd83c2 100644 --- a/src/Characteristics.jl +++ b/src/Characteristics.jl @@ -5,15 +5,8 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module Characteristics - -include("./Behavior.jl") -using .Behavior - -export determineCharacteristics - ## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior -function determineCharacteristics(path::Dict, train::Dict, settings::Dict) +function determineCharacteristics(path::Dict, train::Dict, settings::Settings) movingSection = createMovingSection(path, train[:v_limit]) movingSection = secureBrakingBehavior!(movingSection, train[:a_braking]) movingSection = secureAcceleratingBehavior!(movingSection, settings, train) @@ -123,7 +116,7 @@ function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) end #function secureBrakingBehavior! ## define the intersection velocities between the characterisitc sections to secure accelerating behavior -function secureAcceleratingBehavior!(movingSection::Dict, settings::Dict, train::Dict) +function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Dict) # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards CSs = movingSection[:characteristicSections] @@ -136,7 +129,7 @@ function secureAcceleratingBehavior!(movingSection::Dict, settings::Dict, train: CS[:v_entry] = min(CS[:v_entry], previousCSv_exit) startingPoint[:s] = CS[:s_entry] startingPoint[:v] = CS[:v_entry] - calculateForces!(startingPoint, CSs, CS[:id], "accelerating", train, settings[:massModel]) # traction effort and resisting forces (in N) + calculateForces!(startingPoint, CSs, CS[:id], "accelerating", train, settings.massModel) # traction effort and resisting forces (in N) acceleratingCourse::Vector{Dict} = [startingPoint] # List of data points if CS[:v_entry] < CS[:v_peak] @@ -160,7 +153,7 @@ function secureAcceleratingBehavior!(movingSection::Dict, settings::Dict, train: (CS, acceleratingCourse, stateFlags) = addClearingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the train is not allowed to accelerate because of a previous speed limit end else - if settings[:massModel] == "mass point" || acceleratingCourse[end][:s] > CS[:s_entry] + train[:length] + if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train[:length] break else (CS, acceleratingCourse, stateFlags) = addDiminishingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort @@ -190,7 +183,7 @@ end #function secureAcceleratingBehavior! #= ## define the intersection velocities between the characterisitc sections to secure cruising behavior -function secureCruisingBehavior!(movingSection::Dict, settings::Dict, train::Dict) +function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Dict) # limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak CSs = movingSection[:characteristicSections] @@ -225,7 +218,7 @@ function secureCruisingBehavior!(movingSection::Dict, settings::Dict, train::Dic (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") end else - if settings[:massModel] == "mass point" || cruisingCourse[end][:s] > CS[:s_entry] + train[:length] + if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train[:length] break else (CS, cruisingCourse, stateFlags) = addDiminishingSection!(CS, cruisingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort @@ -246,4 +239,3 @@ function secureCruisingBehavior!(movingSection::Dict, settings::Dict, train::Dic return movingSection end #function secureCruisingBehavior! =# -end #module Characteristics diff --git a/src/EnergySaving.jl b/src/EnergySaving.jl deleted file mode 100644 index 037f9af..0000000 --- a/src/EnergySaving.jl +++ /dev/null @@ -1,1045 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -# INFO: EnergySaving should not be used because it is not completed yet. It was used to show the possibility of calculating different operation modes. -# TODO: It has to be optimized so that each ernergy saving method is working individually for every train on every path. - -# TODO: calculation time for passenger trains on path1 is very long and should be reduced -# TODO from 2022/01/18: Test if enum trainType is working correctly in function calculateRecoveryTime or if only the else-pathis taken -# TODO from 2022/01/19: Are here calculations that should be transferred to Formulary.jl? -# 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 -include("./Behavior.jl") - -# use modules of TrainRunCalc -using .Behavior - -export addOperationModeEnergySaving! - -@enum trainType passenger=1 freight=2 motorCoachTrain=3 - -approximationLevel = 6 # value for approximation to intersections - # TODO: define it in TrainRun and give it to each function? - -## functions for calculating the operation mode for the minimum energy consumption - -# calculate the train run for operation mode "minimum energy consumption" -function addOperationModeEnergySaving!(accumulatedDict::Dict) - if accumulatedDict[:settings][:operationModeMinimumEnergyConsumption] == true - movingSectionMinimumRunningTime = accumulatedDict[:movingSectionMinimumRunningTime] - drivingCourseMinimumRunningTime = accumulatedDict[:drivingCourseMinimumRunningTime] - settings = accumulatedDict[:settings] - train = accumulatedDict[:train] - (movingSectionMinimumEnergyConsumption, drivingCourseMinimumEnergyConsumption)=calculateMinimumEnergyConsumption(movingSectionMinimumRunningTime, drivingCourseMinimumRunningTime, settings, train) - println("The driving course for the lowest energy consumption has been calculated.") - - # accumulate data and create an output dictionary - merge!(accumulatedDict, Dict(:movingSectionMinimumEnergyConsumption => movingSectionMinimumEnergyConsumption, :drivingCourseMinimumEnergyConsumption => drivingCourseMinimumEnergyConsumption)) - else - println("No output for minimum energy consumption has been demanded and so none will be calculated.") - end #if - - return accumulatedDict -end #function addOperationModeEnergySaving! - -function calculateMinimumEnergyConsumption(movingSectionMinimumRunningTime::Dict, drivingCourseMinimumRunningTime::Vector{Dict}, settings::Dict, train::Dict) - # calculate a train run focussing on using the minimum possible energy consumption - # booleans for choosing which methods are used for saving energy - doMethod1=true - #doMethod1=false - - doMethod2=true - #doMethod2=false - - doCombinationOfMethods=true - #doCombinationOfMethods=false - - # create a new driving course for the minimum energy consumption - drivingCourseOriginal = copy(drivingCourseMinimumRunningTime) - - #create a new moving section for the minimum energy consumption - movingSectionOriginal=copyMovingSection(movingSectionMinimumRunningTime) - # 01/09 old not sure if just copy is enough.. : movingSectionOriginal=copy(movingSectionMinimumRunningTime) - CSsOrig::Vector{Dict} = movingSectionOriginal[:characteristicSections] - merge!(movingSectionOriginal, Dict(:energySavingModifications => [])) # list containing all the used energy saving modifications - - # calculate the recovery time - t_recovery=calculateRecoveryTime(movingSectionOriginal[:length], movingSectionOriginal[:t], train) - merge!(movingSectionOriginal, Dict(:t_recovery=>t_recovery)) # total recovery time for energy-saving modifications (in s) - merge!(movingSectionOriginal, Dict(:t_recoveryAvailable => t_recovery)) # still available recovery time for energy-saving modifications (in s) initialized with the total recovery time - - # create arrays for each method with all the available energy saving modifications - energySavingModificationsWithCoasting=Dict[] - energySavingModificationsWithMaximumSpeed=Dict[] - energySavingModificationsWithCombination=Dict[] - - for csId in 1:length(CSsOrig) - # method 1: increase coasting - if doMethod1 == true - modificationType = "increasing coasting" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csId, modificationType, settings, train) - push!(energySavingModificationsWithCoasting, energySavingModification) - end #if doMethod1 - - # method 2: accelerate to a lower v_peak - if doMethod2 == true - modificationType = "decreasing maximum velocity" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csId, modificationType, settings, train) - push!(energySavingModificationsWithMaximumSpeed, energySavingModification) - end #if doMethod2 - - # calculate the combination of the previous methods - if doCombinationOfMethods == true - modificationType = "combination of energy saving methods" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csId, modificationType, settings, train) - push!(energySavingModificationsWithCombination, energySavingModification) - end #if - end # for - - # 01/03 old wit too long calculation time: while movingSectionOriginal[:t_recoveryAvailable] > 0.0 - while movingSectionOriginal[:t_recoveryAvailable] >= 1/(10^approximationLevel) - # compare modifications - ratioMax=0.0 - csIdMax=0 - typeMax="none" - (energySavingModificationsWithMaximumSpeed, ratioMax, csIdMax, typeMax) = findBestModification(energySavingModificationsWithMaximumSpeed, ratioMax, csIdMax, typeMax, movingSectionOriginal[:t_recoveryAvailable]) - (energySavingModificationsWithCoasting, ratioMax, csIdMax, typeMax) = findBestModification(energySavingModificationsWithCoasting, ratioMax, csIdMax, typeMax, movingSectionOriginal[:t_recoveryAvailable]) - (energySavingModificationsWithCombination, ratioMax, csIdMax, typeMax) = findBestModification(energySavingModificationsWithCombination, ratioMax, csIdMax, typeMax, movingSectionOriginal[:t_recoveryAvailable]) - - - # select the most efficient modification and update the original characteristicSection, drivingCourse and movingSection - # in case none of the modifications has a ratio>0 stop the calculation - if typeMax=="none" - break - elseif typeMax=="increasing coasting" - # println("Energy saving modification number ",length(movingSectionOriginal[:energySavingModifications])+1," (coasting) in CS ",csIdMax ," Δt=",energySavingModificationsWithCoasting[csIdMax][:Δt]," ΔE=", energySavingModificationsWithCoasting[csIdMax][:ΔE]," t_recoveryAvailable=", movingSectionOriginal[:t_recoveryAvailable]-energySavingModificationsWithCoasting[csIdMax][:Δt]) - push!(movingSectionOriginal[:energySavingModifications], energySavingModificationsWithCoasting[csIdMax]) - # println("Nr. ",length(movingSectionOriginal[:energySavingModifications]),": typeMax=increasing coasting") - elseif typeMax=="decreasing maximum velocity" - push!(movingSectionOriginal[:energySavingModifications], energySavingModificationsWithMaximumSpeed[csIdMax]) - # println("Nr. ",length(movingSectionOriginal[:energySavingModifications]),": typeMax=decreasing maximum velocity") - elseif typeMax=="combination of energy saving methods" - push!(movingSectionOriginal[:energySavingModifications], energySavingModificationsWithCombination[csIdMax]) - # println("Nr. ",length(movingSectionOriginal[:energySavingModifications]),": typeMax=combination of energy saving methods") - end #if - - movingSectionOriginal[:t_recoveryAvailable] = movingSectionOriginal[:t_recoveryAvailable] - movingSectionOriginal[:energySavingModifications][end][:Δt] - - lastIdOfSelectedCsOriginal = get(CSsOrig[csIdMax][:behaviorSections], :standstill, - get(CSsOrig[csIdMax][:behaviorSections], :braking, - get(CSsOrig[csIdMax][:behaviorSections], :coasting, - get(CSsOrig[csIdMax][:behaviorSections], :cruising, - get(CSsOrig[csIdMax][:behaviorSections], :accelerating, - get(CSsOrig[csIdMax][:behaviorSections], :clearing, - get(CSsOrig[csIdMax][:behaviorSections], :breakFree, - get(CSsOrig[csIdMax][:behaviorSections], :diminishing, - Dict(:dataPoints => [0])))))))))[:dataPoints][end] - - # if there is a diminishing phase its location must be analysed seperately because it could be before accelerating, between accelerating and cruising or after cruising. All the other behavior sections occure in a fixed order. - if haskey(CSsOrig[csIdMax][:behaviorSections], :diminishing) - lastIdOfSelectedCsOriginal = max(lastIdOfSelectedCsOriginal, CSsOrig[csIdMax][:behaviorSections][:diminishing][:dataPoints][end]) - end - - # create new driving course - drivingCourseNew = copy(movingSectionOriginal[:energySavingModifications][end][:drivingCourseModified]) - - #fill up the rest of the driving course with information from the original course - drivingCourseNew[end][:F_T]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:F_T] - drivingCourseNew[end][:R_traction]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:R_traction] - drivingCourseNew[end][:R_wagons]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:R_wagons] - drivingCourseNew[end][:R_train]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:R_train] - drivingCourseNew[end][:R_path]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:R_path] - drivingCourseNew[end][:F_R]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:F_R] - drivingCourseNew[end][:a]=drivingCourseOriginal[lastIdOfSelectedCsOriginal][:a] - - endOfModificationId=drivingCourseNew[end][:i] # is needed for updating the other modified driving courses - difference=endOfModificationId-lastIdOfSelectedCsOriginal - - i=lastIdOfSelectedCsOriginal+1 - while i <= length(drivingCourseOriginal) - push!(drivingCourseNew, copy(drivingCourseOriginal[i])) - drivingCourseNew[end][:i]=length(drivingCourseNew) - drivingCourseNew[end][:t]=drivingCourseNew[end-1][:t]+drivingCourseNew[end][:Δt] - drivingCourseNew[end][:E]=drivingCourseNew[end-1][:E]+drivingCourseNew[end][:ΔE] - drivingCourseNew[end][:W]=drivingCourseNew[end-1][:W]+drivingCourseNew[end][:ΔW] - i=i+1 - end # while - - # replace the original driving course and CS with the new modified ones - drivingCourseOriginal=drivingCourseNew - CSsOrig[csIdMax]=copyCharacteristicSection(movingSectionOriginal[:energySavingModifications][end][:csModified]) - # 01/09 old with copy: CSsOrig[csIdMax]=copy(movingSectionOriginal[:energySavingModifications][end][:csModified]) - movingSectionOriginal[:t]=drivingCourseOriginal[end][:t] # total running time (in s) - movingSectionOriginal[:E]=drivingCourseOriginal[end][:E] # total energy consumption (in Ws) - - # update all the data point references in the behaviour sections of the following characteristic sections and the other modified characteristic sections - if difference!= 0 - # update the data point references in the behaviour sections of the following characteristic sections - allBs=[:breakFree, :clearing, :accelerating, :cruising, :downhillBraking, :diminishing, :coasting, :braking, :standstill] - for csId in csIdMax+1:length(CSsOrig) - for bs in 1: length(allBs) - if haskey(CSsOrig[csId][:behaviorSections], allBs[bs]) - for point in 1:length(CSsOrig[csId][:behaviorSections][allBs[bs]][:dataPoints]) - CSsOrig[csId][:behaviorSections][allBs[bs]][:dataPoints][point] = CSsOrig[csId][:behaviorSections][allBs[bs]][:dataPoints][point]+difference - end - end #if - end #for - end #for - - # update the data points in the following modified charateristic sections and the following points in the driving course - energySavingModificationsWithCoasting = updateEnergySavingModifications!(energySavingModificationsWithCoasting, csIdMax, drivingCourseNew, endOfModificationId, lastIdOfSelectedCsOriginal) - energySavingModificationsWithMaximumSpeed = updateEnergySavingModifications!(energySavingModificationsWithMaximumSpeed, csIdMax, drivingCourseNew, endOfModificationId, lastIdOfSelectedCsOriginal) - energySavingModificationsWithCombination = updateEnergySavingModifications!(energySavingModificationsWithCombination, csIdMax, drivingCourseNew, endOfModificationId, lastIdOfSelectedCsOriginal) - end # if difference - - # modify new CS for the considered methods - # method 1: increase coasting - if doMethod1==true - modificationType = "increasing coasting" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csIdMax, modificationType, settings, train) - energySavingModificationsWithCoasting[csIdMax]=energySavingModification - end #if if doMethod1 - - # method 2: accelerate to a lower v_peak - if doMethod2==true - modificationType = "decreasing maximum velocity" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csIdMax, modificationType, settings, train) - energySavingModificationsWithMaximumSpeed[csIdMax]=energySavingModification - end #if if doMethod - - # combination of both methods - if doCombinationOfMethods==true - modificationType = "combination of energy saving methods" - energySavingModification = modifyCs(movingSectionOriginal, drivingCourseOriginal, csIdMax, modificationType, settings, train) - energySavingModificationsWithCombination[csIdMax]=energySavingModification - end #if doCombinationOfMethods - end # while - - (CSsOrig[end], drivingCourseOriginal) = addStandstill!(CSsOrig[end], drivingCourseOriginal, settings, train, CSsOrig) - - println("t_recoveryAvailable=",movingSectionOriginal[:t_recoveryAvailable]) - return (movingSectionOriginal, drivingCourseOriginal) -end #function calculateMinimumEnergyConsumption - -## copy the different sections the whole path can be devided in -function copyMovingSection(original::Dict) - copiedCSs = Vector{Dict}() - for csId in 1:length(original[:characteristicSections]) - push!(copiedCSs, copyCharacteristicSection(original[:characteristicSections][csId])) - # 01/07 old without copy: push!(copiedCSs, copyCharacteristicSection(original[:characteristicSections][csId])) - end #for - - copiedMS = Dict(:id => original[:id], # identifier - :length => original[:length], # total length (in m) - :s_entry => original[:s_entry], # first position (in m) - :s_exit => original[:s_exit], # last position (in m) - :t => original[:t], # total running time (in s) - :E => original[:E], # total energy consumption (in Ws) - :characteristicSections => copiedCSs) # list of containing characteristic sections - - if haskey(original, :energySavingModifications) # list of containing all the used energy saving modifications - copiedModifications = Dict[] - for modId in 1:length(original[:energySavingModifications]) - push!(copiedModifications, copyEnergySavingModification(original[:energySavingModifications][modId])) - end #for - merge!(copiedMS, Dict(:energySavingModifications => copiedModifications)) - end - - if haskey(original, :t_recovery) # total recovery time for energy-saving modifications (in s) - merge!(copiedMS, Dict(:t_recovery => original[:t_recovery])) - end - - if haskey(original, :t_recoveryAvailable) # still available recovery time for energy-saving modifications (in s) - merge!(copiedMS, Dict(:t_recoveryAvailable => original[:t_recoveryAvailable])) - end - return copiedMS -end #function copyMovingSection - -function copyCharacteristicSection(originalCS::Dict) - allBs=[:breakFree, :clearing, :accelerating, :cruising, :downhillBraking, :diminishing, :coasting, :braking, :standstill] - copiedBSs = Dict() - for bs in 1: length(allBs) - if haskey(originalCS[:behaviorSections], allBs[bs]) - merge!(copiedBSs, Dict(allBs[bs] => originalCS[:behaviorSections][allBs[bs]])) - end #if - end #for - - copiedCS=Dict(:id => originalCS[:id], # identifier - :s_entry => originalCS[:s_entry], # first position (in m) - :s_exit => originalCS[:s_exit], # last position (in m) - :length => originalCS[:length], # total length (in m) - :r_path => originalCS[:r_path], # path resistance (in ‰) - # :behaviorSections => copy(originalCS[:behaviorSections]), # list of containing behavior sections - :behaviorSections => copiedBSs, # list of containing behavior sections - :t => originalCS[:t], # total running time (in s) - :E => originalCS[:E], # total energy consumption (in Ws) - :v_limit => originalCS[:v_limit], # speed limit (in m/s) - :v_peak => originalCS[:v_peak], # maximum reachable speed (in m/s) - :v_entry => originalCS[:v_entry], # maximum entry speed (in m/s) - :v_exit => originalCS[:v_exit], # maximum exit speed (in m/s) - :pointsOfInterest => originalCS[:pointsOfInterest]) # points of interest for which data points should be calculated - - return copiedCS -end # CharacteristicSection - -# smallest section of the path is the behavior section. It relates to the containing data points via their identifier. -function copyBehaviorSection(original::Dict) - bsDataPoints=[] - for i in 1:length(original[:dataPoints]) - push!(bsDataPoints, original[:dataPoints][i]) - end - copiedBS = Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill - :type => original[:type], # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill" - :length => original[:length], # total length (in m) - :s_entry => original[:s_entry], # first position (in m) - :s_exit => original[:s_exit], # last position (in m) - :t => original[:t], # total running time (in s) - :E => original[:E], # total energy consumption (in Ws) - :v_entry => original[:v_entry], # entry speed (in m/s) - :v_exit => original[:v_exit], # exit speed (in m/s) - :dataPoints => bsDataPoints) # list of identifiers of the containing data points - return copiedBS -end #function copyBehaviorSection - -## for the energy saving operation mode it is nesserary to compare different energy saving modifications. These are part of the moving section. -function createEnergySavingModification() - energySavingModification = Dict(:csId => 0, # identifier of the characteristic section - :type => "", # type of energy saving modification: "increasing coasting" "decreasing maximum velocity" or "combination of decreasing maximum velocity and coasting" - :ΔE => 0.0, # saved energy (in Ws) - :Δt => 0.0, # time loss (in s) - :ratio => 0.0, # ratio of ΔE and Δt (in Ws/s) - :csModified => Dict(), # the modified characteristic section - :drivingCourseModified => []) # drivingCourse for the modified characteristic section -end #createEnergySavingModification - -function updateEnergySavingModifications!(energySavingModifications::Vector{Dict}, csIdMax::Integer, drivingCourseNew::Vector{Dict}, endOfModificationId::Integer, lastIdOfSelectedCsOriginal::Integer) - allBs=[:breakFree, :clearing, :accelerating, :cruising, :downhillBraking, :diminishing, :coasting, :braking, :standstill] - difference = endOfModificationId-lastIdOfSelectedCsOriginal - for modNr in csIdMax+1:length(energySavingModifications) - if energySavingModifications[modNr][:ratio]>0 - BSs = energySavingModifications[modNr][:csModified][:behaviorSections] - # update the behavior sections of the modified charateristic section - for bs in 1: length(allBs) - if haskey(BSs, allBs[bs]) - for point in 1:length(BSs[allBs[bs]][:dataPoints]) - BSs[allBs[bs]][:dataPoints][point] = BSs[allBs[bs]][:dataPoints][point] + difference - end - end #if - end #for - - # correct the points of previous CS in the modified driving course. Copy the new driving course till the beginning of the current CS and change total values of the current modified CS data points accordingly - drivingCourseModifiedNew = copy(drivingCourseNew[1:endOfModificationId]) - - i=lastIdOfSelectedCsOriginal+1 - while i <= length(energySavingModifications[modNr][:drivingCourseModified]) - push!(drivingCourseModifiedNew, copy(energySavingModifications[modNr][:drivingCourseModified][i])) - - drivingCourseModifiedNew[end][:i]=length(drivingCourseModifiedNew) - drivingCourseModifiedNew[end][:t]=drivingCourseModifiedNew[end-1][:t]+drivingCourseModifiedNew[end][:Δt] - drivingCourseModifiedNew[end][:E]=drivingCourseModifiedNew[end-1][:E]+drivingCourseModifiedNew[end][:ΔE] - drivingCourseModifiedNew[end][:W]=drivingCourseModifiedNew[end-1][:W]+drivingCourseModifiedNew[end][:ΔW] - i=i+1 - end # while - - energySavingModifications[modNr][:drivingCourseModified] = drivingCourseModifiedNew - end #if - end #for - - return energySavingModifications -end #function updateEnergySavingModifications! - -function copyEnergySavingModification(modificaionOriginal::Dict) - modificaionCopy = Dict(:csId => modificaionOriginal[:csId], # identifier of the characteristic section - :type => modificaionOriginal[:type], # type of energy saving modification: "increasing coasting" "decreasing maximum velocity" or "combination of decreasing maximum velocity and coasting" - :ΔE => modificaionOriginal[:ΔE], # saved energy (in Ws) - :Δt => modificaionOriginal[:Δt], # time loss (in s) - :ratio => modificaionOriginal[:ratio], # ratio of ΔE and Δt (in Ws/s) - :csModified => copyCharacteristicSection(modificaionOriginal[:]), # the modified characteristic section - :drivingCourseModified => copy(modificaionOriginal[:drivingCourseModified])) # drivingCourse for the modified characteristic section - - return modificaionCopy -end # copyEnergySavingModification - -function modifyCs(movingSectionOriginal::Dict, drivingCourseOriginal::Vector{Dict}, csId::Integer, modificationType::String, settings::Dict, train::Dict) - # TODO: refactor and sort this function - CSsOrig::Vector{Dict} = movingSectionOriginal[:characteristicSections] - - if modificationType == "increasing coasting" - # method 1: increase coasting - (characteristicSectionModified, drivingCourseModifiedUntilEndOfModifiedCS, new)=increaseCoastingSection(CSsOrig[csId], drivingCourseOriginal, settings, train, CSsOrig, movingSectionOriginal[:t_recoveryAvailable]) - elseif modificationType == "decreasing maximum velocity" - # method 2: accelerate to a lower v_peak - (characteristicSectionModified, drivingCourseModifiedUntilEndOfModifiedCS, new)=decreaseMaximumVelocity(CSsOrig[csId], drivingCourseOriginal, settings, train, CSsOrig, movingSectionOriginal[:t_recoveryAvailable]) - - elseif modificationType == "combination of energy saving methods" - # calculate the combination of the previous methods - (characteristicSectionModified, drivingCourseModifiedUntilEndOfModifiedCS, new)=combineEnergySavingMethods(CSsOrig[csId], drivingCourseOriginal, settings, train, CSsOrig, movingSectionOriginal[:t_recoveryAvailable]) - else - return createEnergySavingModification() - end - - #energySavingModification = createEnergySavingModification() - if new - energySavingModification = Dict(:csId => csId, # identifier of the characteristic section - :type => modificationType, # type of energy saving modification: "increasing coasting" "decreasing maximum velocity" or "combination of decreasing maximum velocity and coasting" - :csModified => characteristicSectionModified, # the modified characteristic section - :drivingCourseModified => drivingCourseModifiedUntilEndOfModifiedCS) # drivingCourse for the modified characteristic section - - merge!(energySavingModification, Dict(:ΔE => CSsOrig[csId][:E] - energySavingModification[:csModified][:E])) # saved energy (in Ws) - merge!(energySavingModification, Dict(:Δt => energySavingModification[:csModified][:t] - CSsOrig[csId][:t])) # time loss (in s) - - - if energySavingModification[:Δt] <= movingSectionOriginal[:t_recoveryAvailable] && energySavingModification[:ΔE] >= 0.0 - #*** TODO: check why "sign" is needed here - # if modificationType == "combination of energy saving methods" - ratio=sign(energySavingModification[:Δt])*energySavingModification[:ΔE]/energySavingModification[:Δt] # ratio of ΔE and Δt (in Ws/s) - - # else - # ratio = energySavingModification[:ΔE] / energySavingModification[:Δt] # ratio of ΔE and Δt (in Ws/s) - # end - # *** - elseif energySavingModification[:Δt] == 0.0 - ratio = energySavingModification[:ΔE]/0.000000001 - else # Δt is to high or ΔE < 0.0 Ws - ratio = 0.0 - end - merge!(energySavingModification, Dict(:ratio => ratio)) # ratio of ΔE and Δt (in Ws/s) - return energySavingModification - else - return createEnergySavingModification() - end -end #function modifyCs - -function findBestModification(energySavingModifications::Vector{Dict}, ratioMax::AbstractFloat, csIdMax::Integer, typeMax::String, t_recoveryAvailable::AbstractFloat) - for modNr in 1:length(energySavingModifications) - if energySavingModifications[modNr][:ratio] > ratioMax - if energySavingModifications[modNr][:Δt] <= t_recoveryAvailable - ratioMax = energySavingModifications[modNr][:ratio] - csIdMax = energySavingModifications[modNr][:csId] - typeMax = energySavingModifications[modNr][:type] - else # Δt is to high - energySavingModifications[modNr][:ratio]=0.0 - end #if - end #if - end #for - return (energySavingModifications, ratioMax, csIdMax, typeMax) -end #function findBestModification - - -## functions for calculating different energy saving methods that are used in calculateMinimumEnergyConsumption - -function calculateRecoveryTime(s_MS::Real, t_MS::AbstractFloat, train::Dict) - # function for calculating the recovery time that can be used for energy saving - # MS: Moving Section - # 01/05 old without enum: if train[:type]=="motor coach train" - if train[:type] == motorCoachTrain::trainType - if s_MS<= 30000 - c_s=0.0 - else s_MS> 30000 - c_s=0.0006 - end # if s_MS - - if train[:v_limit]<=140/3.6 # unit is m/s - c_t=0.03 - elseif train[:v_limit]<=160/3.6 # unit is m/s - c_t=0.04 - elseif train[:v_limit]<=200/3.6 # unit is m/s - c_t=0.05 - elseif train[:v_limit]<=250/3.6 # unit is m/s - c_t=0.06 - else # train[:v_limit]>120/3.6 # unit is m/s - c_t=0.07 - end # if train[:v_limit] - - t_recovery=s_MS*c_s+t_MS*c_t - return t_recovery - # 01/05 old without enum: elseif train[:type]=="freight" && train[:v_limit]<=120/3.6 # unit is m/s - elseif train[:type] == freight::trainType && train[:v_limit] <= 120/3.6 # unit is m/s - t_recovery1=s_MS*0.0006 +t_MS*0.03 - t_recovery2=s_MS*0.0018 +t_MS*0.0 - t_recovery3=s_MS*0.0 +t_MS*0.04 - t_recovery=max(t_recovery1, t_recovery2, t_recovery3) - - return t_recovery - # 01/05 old without enum: else # train[:trainType]=="passenger" || (train[:trainType]=="freight" && train[:v_limit]>120/3.6) # unit is m/s - else # train[:type] == passenger::trainType || (train[:type] == freight::trainType && train[:v_limit]>120/3.6) # unit is m/s - if s_MS<= 30000 - c_s=0.0 - else s_MS> 30000 - c_s=0.0009 - end # if s_MS - if train[:v_limit]<=140/3.6 # unit is m/s - if train[:m_train]<=300000 # unit is kg - c_t=0.03 - elseif train[:m_train]<=500000 # unit is kg - c_t=0.04 - elseif train[:m_train]<=700000 # unit is kg - c_t=0.04 - else # train[:m_train]>700000 # unit is kg - c_t=0.05 - end # if train[:m_train] - elseif train[:v_limit]<=160/3.6 # unit is m/s - if train[:m_train]<=300000 # unit is kg - c_t=0.03 - elseif train[:m_train]<=500000 # unit is kg - c_t=0.04 - else # train[:m_train]>500000 # unit is kg - c_t=0.0 - end # if train[:m_train] - elseif train[:v_limit]<=200/3.6 # unit is m/s - if train[:m_train]<=300000 # unit is kg - c_t=0.04 - elseif train[:m_train]<=500000 # unit is kg - c_t=0.05 - else # train[:m_train]>500000 # unit is kg - c_t=0.06 - end # if train[:m_train] - else # train[:v_limit]>200/3.6 # unit is m/s - if train[:m_train]<=300000 # unit is kg - c_t=0.05 - elseif train[:m_train]<=500000 # unit is kg - c_t=0.06 - else # train[:m_train]>500000 # unit is kg - c_t=0.07 - end # if train[:m_train] - end # if train[:v_limit] - - c_tMin=s_MS/t_MS*0.0012 - c_t=max(c_t, c_tMin) - - t_recovery=s_MS*c_s+t_MS*c_t - return t_recovery - end # if train[:type] -end #function calculateRecoveryTime - -# TODO: a refactoring caused worse drivingsCourses. see the commented function below -function increaseCoastingSection(csOriginal::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, allCSs::Vector{Dict}, t_recoveryAvailable::AbstractFloat) - BSsOriginal = csOriginal[:behaviorSections] - if (haskey(BSsOriginal, :cruising) || (haskey(BSsOriginal, :diminishing) && get(BSsOriginal, :diminishing, Dict(:dataPoints =>[0]))[:dataPoints][1] > get(BSsOriginal, :accelerating, Dict(:dataPoints =>[0]))[:dataPoints][1])) && haskey(BSsOriginal, :braking) - # check if cruising or diminishing should be reduced for coasting - if haskey(BSsOriginal, :cruising) && haskey(BSsOriginal, :diminishing) - if BSsOriginal[:cruising][:dataPoints][1] > BSsOriginal[:diminishing][:dataPoints][1] - reduceCruising=true - reduceDiminishing=false - else - reduceDiminishing=true - reduceCruising=false - end - elseif haskey(BSsOriginal, :cruising) - reduceCruising=true - reduceDiminishing=false - elseif haskey(BSsOriginal, :diminishing) - reduceDiminishing=true - reduceCruising=false - else - error("in increaseCoastingSection") #TODO - end -#= - # copy csOriginal to csModifiedInitial - csModifiedInitial = copyCharacteristicSection(csOriginal) - BSsModified = csModifiedInitial[:behaviorSections] - - # delete bahavior sections that will be recalculated except breakFree, clearing, accelerating, diminishing - # and rest total running time and energy consumption - if haskey(BSsModified, :coasting) - csModifiedInitial[:E] = csModifiedInitial[:E] - BSsModified[:coasting][:E] - csModifiedInitial[:t] = csModifiedInitial[:t] - BSsModified[:coasting][:t] - delete!(BSsModified, :coasting) - end - if haskey(BSsModified, :braking) - csModifiedInitial[:E] = csModifiedInitial[:E] - BSsModified[:braking][:E] - csModifiedInitial[:t] = csModifiedInitial[:t] - BSsModified[:braking][:t] - delete!(BSsModified, :braking) - end - if haskey(BSsModified, :standstill) - csModifiedInitial[:E] = csModifiedInitial[:E] - BSsModified[:standstill][:E] - csModifiedInitial[:t] = csModifiedInitial[:t] - BSsModified[:standstill][:t] - delete!(BSsModified, :standstill) - end - - - if reduceCruising - cruisingReduction = settings[:stepSize] - # 01/07 test for a better calculation time: cruisingReduction = settings[:stepSize]*100 - - # remove old cruising section - csModifiedInitial[:E] = csModifiedInitial[:E] - BSsModified[:cruising][:E] - csModifiedInitial[:t] = csModifiedInitial[:t] - BSsModified[:cruising][:t] - delete!(BSsModified, :cruising) - - # determine the starting point of saving energy (needed for copying the characteristic section's drivingCourse) - energySavingStartId=get(BSsOriginal, :cruising, Dict(:dataPoints=>[0]))[:dataPoints][1] - if energySavingStartId==0 - error("ERROR at creating a new driving course for energy saving with coasting !") - end - - while cruisingReduction>=settings[:stepSize]/10^approximationLevel - #while cruisingReduction>=settings[:stepSize]/100 - while cruisingReduction>=settings[:stepSize]/10^approximationLevel # will be done once and then depending on approximationLevel repeated with smaller cruisingReduction unless !(drivingCourseModified[end][:v]<=csModified[:v_exit] && drivingCourseModified[end][:s] see below at the end of the while loop - # copy the characteristic section for being modified - csModified = copyCharacteristicSection(csModifiedInitial) - - # copy the driving course till the beginning of energy saving - drivingCourseModified = copy(drivingCourse[1:energySavingStartId]) # List of data points till the start of energy saving - - # calculating the new length of the cruising section - if settings[:stepVariable]=="s in m" # distance step method - s_cruising = BSsOriginal[:cruising][:length] - cruisingReduction - elseif settings[:stepVariable]=="t in s" # time step method - # 09/20 old: doesn't work for non constant cruising -> TODO: should work now - # t_cruising=BSsOriginal[:cruising][:t]-cruisingReduction - # s_cruising=t_cruising*drivingCourseModified[end][:v] - distanceReduction = drivingCourse[BSsOriginal[:cruising][:dataPoints][end]][:v] * cruisingReduction - s_cruising = BSsOriginal[:cruising][:length]-distanceReduction - - elseif settings[:stepVariable]=="v in m/s" # velocity step method - s_cruising=BSsOriginal[:cruising][:length]-cruisingReduction*10 # TODO: or better: *100 ? - end #if - s_cruising=max(0.0, s_cruising) - - # calculate the new and now shorter cruising section - if s_cruising>0.0 - (csModified, drivingCourseModified)=addCruisingSection!(csModified, drivingCourseModified, s_cruising, settings, train, allCSs, "cruising") - end - - # calculate the coasting phase until the point the train needs to brake - (csModified, drivingCourseModified)=addCoastingSection!(csModified, drivingCourseModified, settings, train, allCSs) - - if drivingCourseModified[end][:v] < csModified[:v_exit] || drivingCourseModified[end][:s] > csModified[:s_exit] - # the train reaches v_exit before reaching s_exit. The cruising and coasting sections have to be calculated again with a larger cruising section (so with a smaller reduction of the cruising section) - cruisingReduction=cruisingReduction/10 - else - break - end - end # while cruisingReduction - - # calculate the moving phase between coasting and the end of the CS - if drivingCourseModified[end][:v] > csModified[:v_exit] - #(csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings[:massModel], train, allCSs) - (csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings, train, allCSs) - end - - if t_recoveryAvailable < csModified[:t]-csOriginal[:t] || drivingCourseModified[end][:v] != csModified[:v_exit] || drivingCourseModified[end][:s] != csModified[:s_exit] # time loss is to high and the CS has to be calculated again with larger cruising section (so with a smaller reduction of the cruising section) or v_exit or s_exit are not reached excatly - cruisingReduction=cruisingReduction/10 - else - return (csModified, drivingCourseModified, true) - end - end #while - - - elseif reduceDiminishing - # TODO: At the moment diminishing is reduced similar to the accelerating in decreaseMaximumVelocity. To reduce code the methods for reducing cruising phase and reducing the diminishing phase can be combined in some parts. - csModified = csModifiedInitial - diminishingSection = BSsModified[:diminishing] - - # remove the last diminishing data point - t_diff = drivingCourse[diminishingSection[:dataPoints][end]][:t] - drivingCourse[diminishingSection[:dataPoints][end-1]][:t] - E_diff = drivingCourse[diminishingSection[:dataPoints][end]][:E] - drivingCourse[diminishingSection[:dataPoints][end-1]][:E] - pop!(diminishingSection[:dataPoints]) - - diminishingSection[:v_exit] = drivingCourse[diminishingSection[:dataPoints][end]][:v] # exit speed (in m/s) - diminishingSection[:s_exit] = drivingCourse[diminishingSection[:dataPoints][end]][:s] # last position (in m) - diminishingSection[:length] = diminishingSection[:s_exit] - diminishingSection[:s_entry] # total length (in m) - diminishingSection[:t] = diminishingSection[:t] - t_diff # total running time (in s) - diminishingSection[:E] = diminishingSection[:E] - E_diff # total energy consumption (in Ws) - - # merge!(BSsModified, Dict(:diminishing => diminishingSection)) - csModified[:E] = csModified[:E] - t_diff - csModified[:t] = csModified[:t] - E_diff - - energySavingStartId = diminishingSection[:dataPoints][end] - - if length(diminishingSection[:dataPoints]) == 2 # The diminishing section was only one step. This step is removed and so the complette diminishing section. - delete!(BSsModified, :diminishing) - end - -#_____________ -=# - - if reduceCruising - cruisingReduction = settings[:stepSize] - # 01/07 test for a better calculation time: cruisingReduction = settings[:stepSize]*100 - while cruisingReduction>=settings[:stepSize]/10^approximationLevel - #while cruisingReduction>=settings[:stepSize]/100 - while cruisingReduction>=settings[:stepSize]/10^approximationLevel # will be done once and then depending on approximationLevel repeated with smaller cruisingReduction unless !(drivingCourseModified[end][:v]<=csModified[:v_exit] && drivingCourseModified[end][:s] see below at the end of the while loop - - # create a copy for the characteristic sections drivingCourse - energySavingStartId=get(BSsOriginal, :cruising, Dict(:dataPoints=>[0]))[:dataPoints][1] - if energySavingStartId==0 - error("ERROR at creating a new driving course for energy saving with coasting !") - end - - # copy the driving course till the beginning of energy saving - drivingCourseModified = copy(drivingCourse[1:energySavingStartId]) # List of data points till the start of energy saving - - # calculating the new length of the cruising section - if settings[:stepVariable]=="s in m" # distance step method - s_cruising = BSsOriginal[:cruising][:length] - cruisingReduction - elseif settings[:stepVariable]=="t in s" # time step method - # 09/20 old: doesn't work for non constant cruising -> TODO: should work now - # t_cruising=BSsOriginal[:cruising][:t]-cruisingReduction - # s_cruising=t_cruising*drivingCourseModified[end][:v] - distanceReduction = drivingCourse[BSsOriginal[:cruising][:dataPoints][end]][:v]*cruisingReduction - s_cruising = BSsOriginal[:cruising][:length]-distanceReduction - - elseif settings[:stepVariable]=="v in m/s" # velocity step method - s_cruising=BSsOriginal[:cruising][:length]-cruisingReduction*10 # TODO: or better: *100 ? - end #if - s_cruising=max(0.0, s_cruising) - - # copy csOriginal to csModified - # 12/28 old: csModified=CharacteristicSection(csOriginal[:id], csOriginal[:length], csOriginal[:s_entry], csOriginal[:s_exit], 0.0, 0.0, csOriginal[:v_limit], csOriginal[:v_peak], csOriginal[:v_entry], csOriginal[:v_exit], csOriginal[:r_path], Dict{Symbol, Dict}()) - #TODO after removing the mutable structs: Is it possible to just "copy"? with some changes - csModified=Dict(:id => csOriginal[:id], # identifier - :s_entry => csOriginal[:s_entry], # first position (in m) - :s_exit => csOriginal[:s_exit], # last position (in m) - :length => csOriginal[:length], # total length (in m) - :r_path => csOriginal[:r_path], # path resistance (in ‰) - :behaviorSections => Dict(), # empty list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => csOriginal[:v_limit], # speed limit (in m/s) - :v_peak => csOriginal[:v_peak], # maximum reachable speed (in m/s) - :v_entry => csOriginal[:v_entry], # maximum entry speed (in m/s) - :v_exit => csOriginal[:v_exit], # maximum exit speed (in m/s) - :pointsOfInterest => csOriginal[:pointsOfInterest]) # points of interest for which data points should be calculated - - BSsModified = csModified[:behaviorSections] - if haskey(BSsOriginal, :breakFree) - breakFreeSection=copyBehaviorSection(BSsOriginal[:breakFree]) - merge!(BSsModified, Dict(:breakFree=>breakFreeSection)) - csModified[:E] = csModified[:E] + BSsModified[:breakFree][:E] - csModified[:t] = csModified[:t] + BSsModified[:breakFree][:t] - end - if haskey(BSsOriginal, :clearing) # this section is needed before accelerating if the train wants to accelerate to a speed higher than the limit in a previous CS where parts of the train are still located - clearingSection=copyBehaviorSection(BSsOriginal[:clearing]) - merge!(BSsModified, Dict(:clearing=>clearingSection)) - csModified[:E] = csModified[:E] + BSsModified[:clearing][:E] - csModified[:t] = csModified[:t] + BSsModified[:clearing][:t] - end - if haskey(BSsOriginal, :accelerating) - acceleratingSection=copyBehaviorSection(BSsOriginal[:accelerating]) - merge!(BSsModified, Dict(:accelerating=>acceleratingSection)) - csModified[:E] = csModified[:E] + BSsModified[:accelerating][:E] - csModified[:t] = csModified[:t] + BSsModified[:accelerating][:t] - end - if haskey(BSsOriginal, :diminishing) - diminishingSection=copyBehaviorSection(BSsOriginal[:diminishing]) - merge!(BSsModified, Dict(:diminishing=>diminishingSection)) - csModified[:E] = csModified[:E] + BSsModified[:diminishing][:E] - csModified[:t] = csModified[:t] + BSsModified[:diminishing][:t] - end - - - # calculate the new and now shorter cruising section - if s_cruising>0.0 - (csModified, drivingCourseModified)=addCruisingSection!(csModified, drivingCourseModified, s_cruising, settings, train, allCSs, "cruising") - end - - # calculate the coasting phase until the point the train needs to brake - (csModified, drivingCourseModified)=addCoastingSection!(csModified, drivingCourseModified, settings, train, allCSs) - - if drivingCourseModified[end][:v] < csModified[:v_exit] || drivingCourseModified[end][:s] > csModified[:s_exit] - # the train reaches v_exit before reaching s_exit. The cruising and coasting sections have to be calculated again with a larger cruising section (so with a smaller reduction of the cruising section) - cruisingReduction=cruisingReduction/10 - else - break - end - end # while cruisingReduction - - # calculate the moving phase between coasting and the end of the CS - if drivingCourseModified[end][:v] > csModified[:v_exit] - #(csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings[:massModel], train, allCSs) - (csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings, train, allCSs) - end - - if t_recoveryAvailable < csModified[:t]-csOriginal[:t] || drivingCourseModified[end][:v] != csModified[:v_exit] || drivingCourseModified[end][:s] != csModified[:s_exit] # time loss is to high and the CS has to be calculated again with larger cruising section (so with a smaller reduction of the cruising section) or v_exit or s_exit are not reached excatly - cruisingReduction=cruisingReduction/10 - else - return (csModified, drivingCourseModified, true) - end - end #while - - - elseif reduceDiminishing - # TODO: At the moment diminishing is reduced like the accelerating in decreaseMaximumVelocity. To reduce code the methods for reducing cruising phase and reducing the diminishing phase can be combined in some parts. - - # copy csOriginal to csModified - # 12/28 old: csModified=CharacteristicSection(csOriginal[:id], csOriginal[:length], csOriginal[:s_entry], csOriginal[:s_exit], 0.0, 0.0, csOriginal[:v_limit], csOriginal[:v_peak], csOriginal[:v_entry], csOriginal[:v_exit], csOriginal[:r_path], Dict{Symbol, Dict}()) - csModified=Dict(:id => csOriginal[:id], # identifier - :s_entry => csOriginal[:s_entry], # first position (in m) - :s_exit => csOriginal[:s_exit], # last position (in m) - :length => csOriginal[:length], # total length (in m) - :r_path => csOriginal[:r_path], # path resistance (in ‰) - :behaviorSections => Dict(), # empty list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => csOriginal[:v_limit], # speed limit (in m/s) - :v_peak => csOriginal[:v_peak], # maximum reachable speed (in m/s) - :v_entry => csOriginal[:v_entry], # maximum entry speed (in m/s) - :v_exit => csOriginal[:v_exit], # maximum exit speed (in m/s) - :pointsOfInterest => csOriginal[:pointsOfInterest]) # points of interest for which data points should be calculated - - BSsModified = csModified[:behaviorSections] - if haskey(BSsOriginal, :breakFree) - breakFreeSection=copyBehaviorSection(BSsOriginal[:breakFree]) - merge!(BSsModified, Dict(:breakFree=>breakFreeSection)) - csModified[:E] = csModified[:E] + BSsModified[:breakFree][:E] - csModified[:t] = csModified[:t] + BSsModified[:breakFree][:t] - end - if haskey(BSsOriginal, :clearing) # this section is needed before accelerating if the train wants to accelerate to a speed higher than the limit in a previous CS where parts of the train are still located - clearingSection=copyBehaviorSection(BSsOriginal[:clearing]) - merge!(BSsModified, Dict(:clearing=>clearingSection)) - csModified[:E] = csModified[:E] + BSsModified[:clearing][:E] - csModified[:t] = csModified[:t] + BSsModified[:clearing][:t] - end - if haskey(BSsOriginal, :accelerating) - acceleratingSection=copyBehaviorSection(BSsOriginal[:accelerating]) - merge!(BSsModified, Dict(:accelerating=>acceleratingSection)) - csModified[:E] = csModified[:E] + BSsModified[:accelerating][:E] - csModified[:t] = csModified[:t] + BSsModified[:accelerating][:t] - end - if haskey(BSsOriginal, :cruising) - cruisingSection=copyBehaviorSection(BSsOriginal[:cruising]) - merge!(BSsModified, Dict(:cruising=>cruisingSection)) - csModified[:E] = csModified[:E] + BSsModified[:cruising][:E] - csModified[:t] = csModified[:t] + BSsModified[:cruising][:t] - end - - diminishingSection=copyBehaviorSection(BSsOriginal[:diminishing]) - if length(diminishingSection[:dataPoints]) > 2 - # remove the last diminishing data point - pop!(diminishingSection[:dataPoints]) - - diminishingSection[:v_exit]=drivingCourse[diminishingSection[:dataPoints][end]][:v] # exit speed (in m/s) - diminishingSection[:s_exit]=drivingCourse[diminishingSection[:dataPoints][end]][:s] # last position (in m) - diminishingSection[:length]=diminishingSection[:s_exit]-diminishingSection[:s_entry] # total length (in m) - diminishingSection[:t]=drivingCourse[diminishingSection[:dataPoints][end]][:t]-drivingCourse[diminishingSection[:dataPoints][1]][:t] # total running time (in s) - diminishingSection[:E]=drivingCourse[diminishingSection[:dataPoints][end]][:E]-drivingCourse[diminishingSection[:dataPoints][1]][:E] # total energy consumption (in Ws) - - merge!(BSsModified, Dict(:diminishing => diminishingSection)) - csModified[:E] = csModified[:E] + BSsModified[:diminishing][:E] - csModified[:t] = csModified[:t] + BSsModified[:diminishing][:t] - - energySavingStartId=diminishingSection[:dataPoints][end] - else - # The diminishing section is only one step. This step is removed and if there is a clearing section it will be combined with the new cruising section. - energySavingStartId=get(BSsOriginal, :clearing, get(BSsOriginal, :diminishing, Dict(:dataPoints =>[0])))[:dataPoints][1] - end - - # copy the driving course till the beginning of energy saving - drivingCourseModified = copy(drivingCourse[1:energySavingStartId]) # List of data points till the start of energy saving - - # calculate the coasting phase until the point the train needs to brake - (csModified, drivingCourseModified)=addCoastingSection!(csModified, drivingCourseModified, settings, train, allCSs) - - # calculate the moving phase between coasting and the end of the CS - if drivingCourseModified[end][:v] > csModified[:v_exit] - (csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings, train, allCSs) - end - - if t_recoveryAvailable >= csModified[:t] - csOriginal[:t] - return (csModified, drivingCourseModified, true) - else # time loss is to high. so there is no energy saving modification for this CS with the available recovery time - # TODO: just return false or take smaller steps? - - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end - end - - # there is no energy saving modification for this CS with the available recovery time - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - else - # there is no energy saving modification for this CS because a cruising section AND a braking section are needed to be transformed into a coasting section - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end -end # function increaseCoastingSection - -# method 2 with shortening the accelerating by stepsize -function decreaseMaximumVelocity(csOriginal::Dict, drivingCourse, settings::Dict, train::Dict, allCSs::Vector{Dict}, t_recoveryAvailable::AbstractFloat) - # TODO doesn't work that well alone. works better with combineEnergySavingMethods. why? does a while loop end to early or something like this? - #function decreaseMaximumVelocity(csOriginal::CharacteristicSection, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, allCSs::Vector{CharacteristicSection}, t_recoveryAvailable::AbstractFloat) - BSsOriginal = csOriginal[:behaviorSections] - if haskey(BSsOriginal, :accelerating) && csOriginal[:v_peak] > csOriginal[:v_entry] && csOriginal[:v_peak] > csOriginal[:v_exit] - acceleratingSection = copyBehaviorSection(BSsOriginal[:accelerating]) - - if drivingCourse[acceleratingSection[:dataPoints][end]-1][:v] < csOriginal[:v_exit] - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - # TODO: or calculate a new accelerating phase with v_exit as v_peak? it will be very short, shorter than the step size. - end - - # copy csOriginal to csModified - # 12/28 old: csModified=CharacteristicSection(csOriginal[:id], csOriginal[:length], csOriginal[:s_entry], csOriginal[:s_exit], 0.0, 0.0, csOriginal[:v_limit], csOriginal[:v_peak], csOriginal[:v_entry], csOriginal[:v_exit], csOriginal[:r_path], Dict{Symbol, Dict}()) - csModified=Dict(:id => csOriginal[:id], # identifier - :s_entry => csOriginal[:s_entry], # first position (in m) - :s_exit => csOriginal[:s_exit], # last position (in m) - :length => csOriginal[:length], # total length (in m) - :r_path => csOriginal[:r_path], # path resistance (in ‰) - :behaviorSections => Dict(), # empty list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => csOriginal[:v_limit], # speed limit (in m/s) - :v_peak => csOriginal[:v_peak], # maximum reachable speed (in m/s) - :v_entry => csOriginal[:v_entry], # maximum entry speed (in m/s) - :v_exit => csOriginal[:v_exit], # maximum exit speed (in m/s) - :pointsOfInterest => csOriginal[:pointsOfInterest]) # points of interest for which data points should be calculated - - BSsModified = csModified[:behaviorSections] - if haskey(BSsOriginal, :breakFree) - breakFreeSection=copyBehaviorSection(BSsOriginal[:breakFree]) - merge!(BSsModified, Dict(:breakFree=>breakFreeSection)) - csModified[:E] = csModified[:E] + BSsModified[:breakFree][:E] - csModified[:t] = csModified[:t] + BSsModified[:breakFree][:t] - end - if haskey(BSsOriginal, :diminishing) && BSsModified[:diminishing][:dataPoints][1] < BSsModified[:accelerating][:dataPoints][1] - diminishingSection=copyBehaviorSection(BSsOriginal[:diminishing]) - merge!(BSsModified, Dict(:diminishing=>diminishingSection)) - csModified[:E] = csModified[:E] + BSsModified[:diminishing][:E] - csModified[:t] = csModified[:t] + BSsModified[:diminishing][:t] - end - - if length(acceleratingSection[:dataPoints]) > 2 - if haskey(BSsOriginal, :clearing) - clearingSection=copyBehaviorSection(BSsOriginal[:clearing]) - merge!(BSsModified, Dict(:clearing=>clearingSection)) - csModified[:E] = csModified[:E] + BSsModified[:clearing][:E] - csModified[:t] = csModified[:t] + BSsModified[:clearing][:t] - end - - # remove the last data point from the acceleratingSection - pop!(acceleratingSection[:dataPoints]) - energySavingStartId = acceleratingSection[:dataPoints][end] - - acceleratingSection[:v_exit]=drivingCourse[energySavingStartId][:v] # exit speed (in m/s) - acceleratingSection[:s_exit]=drivingCourse[energySavingStartId][:s] # last position (in m) - acceleratingSection[:length]=acceleratingSection[:s_exit]-acceleratingSection[:s_entry] # total length (in m) - acceleratingSection[:t]=drivingCourse[energySavingStartId][:t]-drivingCourse[acceleratingSection[:dataPoints][1]][:t] # total running time (in s) - acceleratingSection[:E]=drivingCourse[energySavingStartId][:E]-drivingCourse[acceleratingSection[:dataPoints][1]][:E] # total energy consumption (in Ws) - - merge!(BSsModified, Dict(:accelerating=>acceleratingSection)) - csModified[:E] = csModified[:E] + acceleratingSection[:E] - csModified[:t] = csModified[:t] + acceleratingSection[:t] - - else - # The accelerating section is only one step. This step is removed and if there is a clearing section it will be combined with the new cruising section. - energySavingStartId=get(BSsOriginal, :clearing, get(BSsOriginal, :accelerating, Dict(:dataPoints =>[0])))[:dataPoints][1] - end - - # TODO: should v_peak be reduced or is it enough to pop the data points? - # characteristicSection[:v_peak]=drivingCourse[end][:v] # setting v_peak to the last data point's velocity which is the highest reachable value in this characteristic section - - # copy the drivingCourse till the beginning of energy saving - drivingCourseModified = copy(drivingCourse[1:energySavingStartId]) # List of data points till the start of energy saving - - s_braking = calcBrakingDistance(drivingCourseModified[end][:v], csModified[:v_exit], train[:a_braking]) - s_cruising = csModified[:s_exit]-drivingCourseModified[end][:s]-s_braking - - if s_cruising > 1/10^approximationLevel - # 01/09 old if s_cruising > 0.001 - (csModified, drivingCourseModified)=addCruisingSection!(csModified, drivingCourseModified, s_cruising, settings, train, allCSs, "cruising") - end #if - - - # s_brakingAfterCruising=ceil((csModified[:v_exit]^2-drivingCourseModified[end][:v]^2)/2/train[:a_braking], digits=10) # TODO: check if s_braking and s_brakingAfterCruising are really always the same - if drivingCourseModified[end][:v]>csModified[:v_exit] - #(csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings[:massModel], train, allCSs) - (csModified, drivingCourseModified)=addBrakingSection!(csModified, drivingCourseModified, settings, train, allCSs) - - elseif drivingCourseModified[end][:s]0.001 - # if (csModified[:s_exit]-drivingCourseModified[end][:s])>10^(-approximationLevel) - # println("INFO: The end of new CS",csModified[:id]," is not reached while saving energy with lowering v_peak.") - # println(" Therefore the calculation of this method can not continue for this CS.") - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end - println("WARNING: The end of new CS",csModified[:id]," is not reached while saving energy with lowering v_peak.") - println(" Therefore s=",drivingCourseModified[end][:s]," will be set s_exit=",csModified[:s_exit]," because the difference is only ",csModified[:s_exit]-drivingCourseModified[end][:s]," m.") - println(" v=",drivingCourseModified[end][:v]," m/s v_exit=",csOriginal[:v_exit] ," m/s") - - drivingCourseModified[end][:s] = csModified[:s_exit] # rounding up to s_exit - drivingCourseModified[end][:Δs] = drivingCourseModified[end][:s] - drivingCourseModified[end-1][:s] - end #if - - if t_recoveryAvailable >= csModified[:t] - csOriginal[:t] - - return (csModified, drivingCourseModified, true) - else # time loss is to high. so there is no energy saving modification for this CS with the available recovery time - # 09/06 old: else # time loss is to high and the CS has to be calculated again with larger accelerating section (so with a smaller reduction of the accelerating section) - # 09/06 old: acceleratingReduction=min(acceleratingReduction/10, csModified[:v_peak]-csModified[:v_entry], csModified[:v_peak]-csModified[:v_exit]) - # TODO: just return false or take smaller steps? - - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end - - # 09/06 old: end #while - # - # 09/06 old: # there is no energy saving modification for this CS with the available recovery time - # old: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - # 12/29 new, now not with empty but with original CS and DC: return (csOriginal, drivingCourse, false) - - - else - # there is no energy saving modification for this CS because v_peak can not be lowered below v_entry or v_exit or because there is no accelerating section that can be transformed into a cruising section - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end #if haskey -end # function decreaseMaximumVelocity - -# combination of method 1 and method 2 -function combineEnergySavingMethods(csOriginal::Dict, drivingCourse::Vector{Dict}, settings::Dict, train::Dict, allCSs::Vector{Dict}, t_recoveryAvailable::AbstractFloat) - BSsOriginal = csOriginal[:behaviorSections] - # if haskey(BSsOriginal, :accelerating) && (haskey(BSsOriginal, :braking) || haskey(BSsOriginal, :coasting)) && csOriginal[:v_peak]>csOriginal[:v_entry] && csOriginal[:v_peak]>csOriginal[:v_exit] - if haskey(BSsOriginal, :accelerating) && (haskey(BSsOriginal, :braking) || haskey(BSsOriginal, :coasting)) && drivingCourse[get(BSsOriginal, :accelerating, Dict(:dataPoints =>[0]))[:dataPoints][end]][:v] > max(csOriginal[:v_entry], csOriginal[:v_exit]) - # copy the characteristic section - csCombined = copyCharacteristicSection(csOriginal) - - # copy the drivingCourse - drivingCourseCombined = copy(drivingCourse) - - ΔE=0.0 # saved energy (in Ws) - Δt=0.0 # time loss (in s) - #while (Δt0.0))# && Δt>0.0)) - else - # there is no energy saving modification for this CS because v_peak can not be lowered below v_entry or v_exit or because there is no accelerating section and braking section or coasting section that can be transformed into a cruising section or coasting section - # 12/29 old, now not with empty but with original CS and DC: return (Dict(), [], false) # TODO: Does the empty CS-Dict need default attributes? - return (csOriginal, drivingCourse, false) - end #if -end #function combineEnergySavingMethods - -end #module EnergySaving diff --git a/src/Export.jl b/src/Export.jl index e88468a..7113866 100644 --- a/src/Export.jl +++ b/src/Export.jl @@ -5,32 +5,26 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module Export - -using CSV, DataFrames, Dates - -export exportToCsv - -function exportToCsv(runningTime::AbstractFloat, settings::Dict) +function exportToCsv(runningTime::AbstractFloat, settings::Settings) createCsvFile(runningTime, settings) return true end -function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Dict) +function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Settings) createCsvFile(dataPointsToExport, settings) return true end function exportToCsv(output::Dict) - if output[:settings][:typeOfOutput] == "CSV" + if output[:settings][:outputFormat] == "CSV" pathName = output[:path][:name] trainName = output[:train][:name] if output[:settings][:operationModeMinimumRunningTime] == true operationMode = "minimum running time" - if output[:settings][:detailOfOutput] == "points of interest" + if output[:settings][:outputDetail] == "points of interest" dataPointsToExport = output[:pointsOfInterestMinimumRunningTime] else dataPointsToExport = output[:drivingCourseMinimumRunningTime] @@ -39,7 +33,7 @@ function exportToCsv(output::Dict) end if output[:settings][:operationModeMinimumEnergyConsumption] == true operationMode = "minimum energy consumption" - if output[:settings][:detailOfOutput] == "points of interest" + if output[:settings][:outputDetail] == "points of interest" dataPointsToExport = output[:pointsOfInterestMinimumEnergyConsumption] else dataPointsToExport = output[:drivingCourseMinimumEnergyConsumption] @@ -51,15 +45,15 @@ function exportToCsv(output::Dict) return false end #function exportToCsv -function createCsvFile(runningTime::AbstractFloat, settings::Dict) +function createCsvFile(runningTime::AbstractFloat, settings::Settings) # create DataFrame with running time information df = DataFrame(column1=["t (in s)", runningTime]) - # save DataFrame as a CSV-file at csvDirectory + # save DataFrame as a CSV-file at outputDir date = Dates.now() dateString = Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - csvFilePath = settings[:csvDirectory]*"/"*dateString*"_RunningTime.csv" + csvFilePath = settings[:outputDir]*"/"*dateString*"_RunningTime.csv" CSV.write(csvFilePath, df, header=false) println("The output CSV file has been created at ",csvFilePath) @@ -68,8 +62,8 @@ function createCsvFile(runningTime::AbstractFloat, settings::Dict) end #function createCsvFile -function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Dict) - detailOfOutput = settings[:detailOfOutput] +function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Settings) + outputDetail = settings[:outputDetail] header = ["i", "behavior", "Δs (in m)", "s (in m)", "Δt (in s)","t (in s)","Δv (in m/s)","v (in m/s)","F_T (in N)","F_R (in N)","R_path (in N)","R_train (in N)","R_traction (in N)","R_wagons (in N)", "ΔW (in Ws)","W (in Ws)","ΔE (in Ws)","E (in Ws)","a (in m/s^2)"] columnSymbols = [:i, :behavior, :Δs, :s, :Δt, :t, :Δv, :v, :F_T, :F_R, :R_path, :R_train, :R_traction, :R_wagons, :ΔW, :W, :ΔE, :E, :a] @@ -85,8 +79,8 @@ function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Dict) end # for - # combine the columns in a data frame and saving it as a CSV-file at csvDirectory - if detailOfOutput == "driving course" || detailOfOutput == "points of interest" + # combine the columns in a data frame and saving it as a CSV-file at outputDir + if outputDetail == "driving course" || outputDetail == "points of interest" df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) else @@ -95,7 +89,7 @@ function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Dict) date = Dates.now() dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - csvFilePath=settings[:csvDirectory]*"/"*dateString*"_DataPoints.csv" + csvFilePath=settings[:outputDir]*"/"*dateString*"_DataPoints.csv" CSV.write(csvFilePath, df, header=false) println("The output CSV file has been created at ",csvFilePath) @@ -103,12 +97,12 @@ function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Dict) end #function createCsvFile -function createCsvFile(dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Dict) - detailOfOutput = settings[:detailOfOutput] +function createCsvFile(dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Settings) + outputDetail = settings[:outputDetail] - massModel = settings[:massModel] - stepVariable = settings[:stepVariable] - stepSize = string(settings[:stepSize]) + massModel = settings.massModel + stepVariable = settings.stepVariable + stepSize = string(settings.stepSize) # create accumulated data block accumulatedData = Array{Any, 1}[] @@ -135,15 +129,15 @@ function createCsvFile(dataPointsToExport::Vector{Dict}, operationMode::String, end end # for - # combine the columns in a data frame and saving it as a CSV-file at csvDirectory + # combine the columns in a data frame and saving it as a CSV-file at outputDir df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) date = Dates.now() dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") if operationMode == "minimum running time" - csvFilePath=settings[:csvDirectory]*"/"*dateString*"_MinimumRunningTime.csv" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:csvDirectory]*"/"*dateString*"_MinimumEnergyConsumption.csv" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" else # should not be possible end @@ -156,20 +150,20 @@ end #function createCsvFile #= -function createCsvFile(movingSection::Dict, dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Dict) - detailOfOutput = settings[:detailOfOutput] +function createCsvFile(movingSection::Dict, dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Settings) + outputDetail = settings[:outputDetail] - massModel = settings[:massModel] - stepVariable = settings[:stepVariable] - stepSize = string(settings[:stepSize]) + massModel = settings.massModel + stepVariable = settings.stepVariable + stepSize = string(settings.stepSize) # create accumulated data block accumulatedData = Array{Any, 1}[] - if detailOfOutput == "minimal" + if outputDetail == "minimal" push!(accumulatedData, ["s (in m)", "t (in s)","E (in Ws)"]) # push header to accumulatedData row = [movingSection[:length], movingSection[:t], movingSection[:E]] push!(accumulatedData, row) # push row to accumulatedData - elseif detailOfOutput == "driving course" || detailOfOutput == "points of interest" + elseif outputDetail == "driving course" || outputDetail == "points of interest" push!(accumulatedData, ["i", "behavior", "Δs (in m)", "s (in m)", "Δt (in s)","t (in s)","Δv (in m/s)","v (in m/s)","F_T (in N)","F_R (in N)","R_path (in N)","R_train (in N)","R_traction (in N)","R_wagons (in N)", "ΔW (in Ws)","W (in Ws)","ΔE (in Ws)","E (in Ws)","a (in m/s^2)"]) # push header to accumulatedData for point in dataPointsToExport row = [point[:i], point[:behavior], point[:Δs], point[:s], point[:Δt], point[:t], point[:Δv], point[:v], point[:F_T], point[:F_R], point[:R_path], point[:R_train], point[:R_traction], point[:R_wagons], point[:ΔW], point[:W], point[:ΔE], point[:E], point[:a]] @@ -194,19 +188,19 @@ function createCsvFile(movingSection::Dict, dataPointsToExport::Vector{Dict}, op end end # for - # combine the columns in a data frame and saving it as a CSV-file at csvDirectory - if detailOfOutput == "minimal" + # combine the columns in a data frame and saving it as a CSV-file at outputDir + if outputDetail == "minimal" df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3]) - elseif detailOfOutput=="driving course" || detailOfOutput == "points of interest" + elseif outputDetail=="driving course" || outputDetail == "points of interest" df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) end date = Dates.now() dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") if operationMode == "minimum running time" - csvFilePath=settings[:csvDirectory]*"/"*dateString*"_MinimumRunningTime.csv" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:csvDirectory]*"/"*dateString*"_MinimumEnergyConsumption.csv" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" else # should not be possible end @@ -216,5 +210,3 @@ function createCsvFile(movingSection::Dict, dataPointsToExport::Vector{Dict}, op return true end #function createCsvFile =# - -end #module Export diff --git a/src/Formulary.jl b/src/Formulary.jl index 2f5f1f0..1be57a6 100644 --- a/src/Formulary.jl +++ b/src/Formulary.jl @@ -5,8 +5,6 @@ # __copyright__ = "2022" # __license__ = "ISC" -module Formulary - ######################### ## literature the driving dynamics equations are based on: ## @@ -21,17 +19,6 @@ module Formulary ## isbn = {978-3-777-10462-1}, ## publisher = {Eurailpress DVV Media Group}, ## } -## @article{Jaekel:2014, -## author = {Jaekel, Birgit and Albrecht, Thomas}, -## year = {2014}, -## title = {Comparative analysis of algorithms and models for train running simulation}, -## journal = {Journal of Rail Transport Planning \& Management}, -## doi = {10.1016/j.jrtpm.2014.06.002}, -## volume = {4}, -## number = {1-2}, -## pages = {14--27}, -## publisher = {Elsevier}, -## } ## @Book{Wende:2003, ## author = {Wende, Dietrich}, ## date = {2003}, @@ -41,25 +28,10 @@ module Formulary ## } ######################### -# export resisting forces and acceleration -export calcTractionUnitResistance, calcWagonsResistance, calcForceFromCoefficient, calcAcceleration, - -# export step sizes in different units -calc_Δs_with_Δt, calc_Δs_with_Δv, -calc_Δt_with_Δs, calc_Δt_with_Δv, calc_Δt_with_constant_v, -calc_Δv_with_Δs, calc_Δv_with_Δt, -calc_ΔW, calc_ΔE, - -# export braking information -calcBrakingDistance, calcBrakingStartVelocity, calcBrakingAcceleration - +approxLevel = 6 v00 = 100/3.6 # velocity factor (in m/s) g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2 -approximationLevel = 6 # value for approximation to intersections TODO further explanation (e.g. approximationLevel = 3 -> with stepSize 10 m the approximation will be calculated accurate on 10 mm ; 1s -> 1 ms; 1 km/h -> 3.6 mm/s) -# TODO: necessary here? - - ## calculate forces #TODO: replace the ? ? ? @@ -213,8 +185,8 @@ function calc_ΔW(F_T_prev::Real, Δs::Real) end #function calc_ΔW function calc_ΔE(ΔW::Real) - # simplified equation is based on [Jaekel:2014, page 6] - + # simplified equation + # TODO! # ΔW: mechanical work in this step (in Ws) ΔE = ΔW # energy consumption in this step (in Ws) return ΔE @@ -228,8 +200,8 @@ function calcBrakingDistance(v_start::Real, v_end::Real, a_braking::Real) # a_braking: constant braking acceleration (in m/s^2) s_braking = (v_end^2 - v_start^2) /2 /a_braking # braking distance (in m) # TODO: also possible: calc_Δs_with_Δv(v_end-v_start, a_braking, v_start) -# return max(0.0, ceil(s_braking, digits=approximationLevel)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors - return max(0.0, ceil(s_braking, digits=approximationLevel +1)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors +# return max(0.0, ceil(s_braking, digits=approxLevel)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors + return max(0.0, ceil(s_braking, digits=approxLevel +1)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors end #function calcBrakingDistance function calcBrakingStartVelocity(v_end::Real, a_braking::Real, s_braking::Real) @@ -239,8 +211,8 @@ function calcBrakingStartVelocity(v_end::Real, a_braking::Real, s_braking::Real) # a_braking: constant braking acceleration (in m/s^2) # s_braking: braking distance (in Ws) v_start = sqrt(v_end^2 - 2*a_braking *s_braking) # braking start velocity (in m/s) -# return floor(v_start, digits=approximationLevel) - return floor(v_start, digits=approximationLevel +1) +# return floor(v_start, digits=approxLevel) + return floor(v_start, digits=approxLevel +1) end #function calcBrakingStartVelocity function calcBrakingAcceleration(v_start::Real, v_end::Real, s_braking::Real) @@ -252,5 +224,3 @@ function calcBrakingAcceleration(v_start::Real, v_end::Real, s_braking::Real) a_braking = (v_end^2 - v_start^2) /2 /s_braking # constant braking acceleration (in m/s^2) return a_braking end #function calcBrakingAcceleration - -end #module Formulary diff --git a/src/Import.jl b/src/Import.jl index baeb5e8..aae95af 100644 --- a/src/Import.jl +++ b/src/Import.jl @@ -5,21 +5,14 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module Import - -import YAML - -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) +function importYamlFiles(trainDirectory::String, pathDirectory::String) train = importFromYaml(:train, trainDirectory) path = importFromYaml(:path, pathDirectory) - settings = importFromYaml(:settings, settingsDirectory) - return (train, path, settings) + return (train, path) end #function importYamlFiles """ @@ -40,5 +33,3 @@ function importFromYaml(dataType::Symbol, directory::String) end return dictionary end # function importFromYaml - -end # module Input diff --git a/src/Output.jl b/src/Output.jl index bf2fd21..a21c12e 100644 --- a/src/Output.jl +++ b/src/Output.jl @@ -5,15 +5,11 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -module Output - -export createOutput - -function createOutput(train::Dict, settings::Dict, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) - if settings[:detailOfOutput] == "running time" +function createOutput(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) + if settings.outputDetail == :running_time output = movingSection[:t] # TODO: or use drivingCourse[end][:t] - elseif settings[:detailOfOutput] == "points of interest" + elseif settings.outputDetail == :points_of_interest # add points of interest if haskey(path, :pointsOfInterest) output = Vector{Dict}() @@ -28,10 +24,10 @@ function createOutput(train::Dict, settings::Dict, path::Dict, movingSection::Di end end - elseif settings[:detailOfOutput] == "driving course" + elseif settings.outputDetail == :driving_course output = drivingCourse - elseif settings[:detailOfOutput] == "everything" + elseif settings.outputDetail == :everything output = Dict{Symbol,Any}() merge!(output, Dict(:train => train, :path => path, :settings => settings)) @@ -71,7 +67,7 @@ function createOutput(train::Dict, settings::Dict, path::Dict, movingSection::Di end #= -function createOutputDict(train::Dict, settings::Dict, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) +function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) outputDict = Dict{Symbol,Any}() merge!(outputDict, Dict(:train => train, :path => path, :settings => settings)) @@ -108,5 +104,3 @@ function createOutputDict(train::Dict, settings::Dict, path::Dict, movingSection return outputDict end # function createOutputDict =# - -end # module Output diff --git a/src/TrainRun.jl b/src/TrainRun.jl index aaf7f2f..2f614fe 100644 --- a/src/TrainRun.jl +++ b/src/TrainRun.jl @@ -1,55 +1,30 @@ #!/usr/bin/env julia # -*- coding: UTF-8 -*- # __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" +# __author__ = "Max Kannenberg, Martin Scheidt" # __copyright__ = "2020-2022" # __license__ = "ISC" +__precompile__(true) module TrainRun -# include main module TrainRunCalc -include("./TrainRunCalc.jl") +## loading external packages +using YAML, JSONSchema, CSV, DataFrames, Dates -# include additional modules -include("./Import.jl") -include("./Export.jl") +## include package files +include("types.jl") +include("formulary.jl") +include("characteristics.jl") +include("behavior.jl") +include("output.jl") +include("import.jl") +include("export.jl") +include("calc.jl") -# include additional modules that are not recommended to use in this state -include("./AdditionalOutput.jl") -include("./EnergySaving.jl") - -# use main module TrainRunCalc -using .TrainRunCalc - -# use additional modules -using .Import -using .Export - -# use additional modules that are not recommended to use in this state -using .AdditionalOutput -using .EnergySaving - -# main function -export trainRun, - -# import functions -importYamlFiles, importFromYaml, - -# functions for saving energy that are not recommended to use in this state -addOperationModeEnergySaving!, - -# export functions -exportToCsv, - -# functions for visualising results that are not recommended to use in this state -plotResults, plotDrivingCourse, printImportantValues, printSectionInformation - -# approximationLevel = 6 # value for approximation to intersections - # TODO: define it here and give it to each function? (Behavior, EnergySaving, ..) - -""" - TODO: Package description -""" +export +## Interface +trainRun, Settings, exportToCsv +## main functions end # module TrainRun diff --git a/src/Validate.jl b/src/Types.jl similarity index 87% rename from src/Validate.jl rename to src/Types.jl index 2c51531..3419b44 100644 --- a/src/Validate.jl +++ b/src/Types.jl @@ -1,31 +1,120 @@ #!/usr/bin/env julia # -*- coding: UTF-8 -*- # __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" +# __author__ = "Max Kannenberg, Martin Scheidt" # __copyright__ = "2022" # __license__ = "ISC" -# TODO: 2022-04-07: if EnergySaving should be used. The train type has do be defined and checked -module Validate +""" + Settings(file) -export checkAndSetInput! +Settings is a datastruture for calculation context. +The function Settings() will create a set of settings for the train run calculation. +`file` is optinal may be used to load settings in the YAML format. +# Example +```jldoctest +julia> my_settings = Settings() # will generate default settings +Settings(mass_point, :distance, 20, 3, running_time, julia_dict, ".") +``` +""" +struct Settings -approximationLevel = 6 # value for approximation to intersections TODO further explanation + massModel::Symbol # model type of train mass ":mass_point" or ":homogeneous_strip". + stepVariable::Symbol # variable of the linear multistep method: ":distance", ":time" or ":velocity". + stepSize::Real # step size, unit depends on stepVariable - :distance in meter, time in seconds and velocity in meter/second. + approxLevel::Int # value for approximation; used when rounding or interating. + outputDetail::Symbol # single Float() ":running_time", Array() of ":points_of_interest", + # complete Array() ":driving_course", or Dict() ":everything". + outputFormat::Symbol # output as ":julia_dict" or as ":csv". + outputDir::String # if outputFormat is not ":julia_dict". + + ## constructor + function Settings(file="DEFAULT") + + ## default values + massModel = :mass_point + stepVariable = :distance + stepSize = 20 + approxLevel = 3 + outputDetail = :running_time + outputFormat = :julia_dict + outputDir = "." + + if file != "DEFAULT" + ## JSON schema for YAML-file validation + schema = Schema("""{ + "properties": { + "massModel": { + "description": "type of train model", + "type": "string", + "enum": [ "mass_point", "homogeneous_strip" ] + }, + "stepVariable": { + "description": "variable of the linear multistep method", + "type": "string", + "enum": [ "distance", "time", "velocity" ] + }, + "stepSize": { + "description": "step size acording to stepVariable", + "type": "number", + "exclusiveMinimum": 0 + }, + "outputDetail": { + "description": "Selecting the detail of the result", + "type": "string", + "enum": [ "running_time", "points_of_interest", "driving_course", "everything" ] + }, + "outputFormat": { + "description": "Output format", + "type": "string", + "enum": [ "julia_dict", "csv" ] + }, + "outputDir": { + "description": "Path for the CSV export", + "type": "string" + } + } + }""") + + settings = YAML.load(open(file))["settings"] + + ## validate the loaded file + try + validate(schema, settings) + catch err + println("Could not load settings file $file.\n Format is not recognized - using default as fall back.") + settings = Dict() + end + + ## set the variables if they exist in "settings" + haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing + haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing + haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing + haskey(settings, "approxLevel") ? approxLevel = settings["approxLevel"] : nothing + haskey(settings, "outputDetail") ? outputDetail = Symbol(settings["outputDetail"]) : nothing + haskey(settings, "outputFormat") ? outputFormat = Symbol(settings["outputFormat"]) : nothing + haskey(settings, "outputDir") ? outputDir = settings["outputDir"] : nothing + end + + new(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) + + end #function Settings() # constructor + +end #struct Settings """ Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. """ -function checkAndSetInput!(train::Dict, path::Dict, settings::Dict) - checkAndSetTrain!(train) - checkAndSetPath!(path) - checkAndSetSettings!(settings) +function checkAndSetInput!(train::Dict, path::Dict, settings::Settings) + checkAndSetTrain!(train) + checkAndSetPath!(path) - if settings[:detailOfOutput] == "points of interest" && !haskey(path, :pointsOfInterest) - settings[:detailOfOutput] = "driving course" - println("INFO at checking the input for settings and path: settings[:detailOfOutput] is 'points of interest' but the path does not have a list for pointsOfInterest. Therefore the detailOfOut is changed to 'driving course'.") - end - return (train, path, settings) + if settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) + throw(DomainError(settings.outputDetail, "INFO at checking the input for settings and path:\n + settings[:outputDetail] is 'points of interest' but the path does not for pointsOfInterest.")) + end + return (train, path) end #function checkAndSetInput! """ @@ -102,39 +191,6 @@ function checkAndSetPath!(path::Dict) return path end # function checkAndSetPath! - -## settings for the calculation -function checkAndSetSettings!(settings::Dict) - # check settings information from input dictionary - - 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" - 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 - - 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"? - - # 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, defaultValue::Bool) if haskey(dictionary,key) && dictionary[key]!=nothing if typeof(dictionary[key]) != Bool @@ -186,7 +242,7 @@ function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictiona 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 ? + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? delete!(dictionary, alternativeKey) println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) end @@ -226,7 +282,7 @@ function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictiona 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 ? + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? delete!(dictionary, alternativeKey) println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) end @@ -261,7 +317,7 @@ function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::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) + if difference > 1/(10^approxLevel) error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") end else @@ -332,7 +388,7 @@ function checkAndSetSpeedLimit!(train::Dict) 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 ? + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? delete!(train, :v_limit_kmh) println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." ) end @@ -388,7 +444,7 @@ function checkAndSetRotationMassFactors!(train::Dict) 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) + if difference > 1/(10^approxLevel) error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") end end @@ -717,18 +773,6 @@ function checkAndSetPOIs!(path::Dict) return path end #function checkAndSetPOIs! -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 = [] @@ -753,5 +797,3 @@ function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol} end end end #function informAboutUnusedKeys - -end # module Validate diff --git a/data/paths/path_1_10km_nConst_vConst.yaml b/test/data/paths/const.yaml similarity index 100% rename from data/paths/path_1_10km_nConst_vConst.yaml rename to test/data/paths/const.yaml diff --git a/data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml b/test/data/paths/realworld.yaml similarity index 100% rename from data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml rename to test/data/paths/realworld.yaml diff --git a/data/paths/path_2_10km_nVar_vConst.yaml b/test/data/paths/slope.yaml similarity index 100% rename from data/paths/path_2_10km_nVar_vConst.yaml rename to test/data/paths/slope.yaml diff --git a/data/paths/path_3_10km_nConst_vVar.yaml b/test/data/paths/speed.yaml similarity index 100% rename from data/paths/path_3_10km_nConst_vVar.yaml rename to test/data/paths/speed.yaml diff --git a/test/data/settings/csv_export.yaml b/test/data/settings/csv_export.yaml new file mode 100644 index 0000000..5cd9517 --- /dev/null +++ b/test/data/settings/csv_export.yaml @@ -0,0 +1,5 @@ +%YAML 1.2 +--- +settings: + outputFormat: "csv" # output as "julia_dict" or as "csv" + outputDir: "." # used if other outputFormat than "julia dict" diff --git a/test/data/settings/detail.yaml b/test/data/settings/detail.yaml new file mode 100644 index 0000000..7b55da7 --- /dev/null +++ b/test/data/settings/detail.yaml @@ -0,0 +1,9 @@ +%YAML 1.2 +--- +settings: +# default settings for the calculation + stepSize: 5 # step size, unit depends on stepVariable - distance in meter, time in seconds and velocity in meter/second. + approxLevel: 6 # value for approximation; used when rounding or interating + outputDetail: "running_time" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything" + outputFormat: "julia_dict" # output as "julia_dict" or as "csv" + outputDir: "." # used if other outputFormat than "julia dict" diff --git a/test/data/settings/driving_course.yaml b/test/data/settings/driving_course.yaml new file mode 100644 index 0000000..46ac088 --- /dev/null +++ b/test/data/settings/driving_course.yaml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +settings: + outputDetail: "driving_course" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything" diff --git a/test/data/settings/strip.yaml b/test/data/settings/strip.yaml new file mode 100644 index 0000000..57c8abe --- /dev/null +++ b/test/data/settings/strip.yaml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +settings: + massModel: "homogeneous_strip" # type of train model used: "mass_point" or "homogeneous_strip" diff --git a/test/data/settings/time.yaml b/test/data/settings/time.yaml new file mode 100644 index 0000000..c182d22 --- /dev/null +++ b/test/data/settings/time.yaml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +settings: + stepVariable: "time" # variable of the linear multistep method: "distance", "time" or "velocity" diff --git a/test/data/settings/time_strip.yaml b/test/data/settings/time_strip.yaml new file mode 100644 index 0000000..bc8bed2 --- /dev/null +++ b/test/data/settings/time_strip.yaml @@ -0,0 +1,5 @@ +%YAML 1.2 +--- +settings: + stepVariable: "time" # variable of the linear multistep method: "distance", "time" or "velocity" + massModel: "homogeneous_strip" # type of train model used: "mass_point" or "homogeneous_strip" diff --git a/test/data/settings/velocity.yaml b/test/data/settings/velocity.yaml new file mode 100644 index 0000000..273918f --- /dev/null +++ b/test/data/settings/velocity.yaml @@ -0,0 +1,5 @@ +%YAML 1.2 +--- +settings: + stepVariable: "velocity" # variable of the linear multistep method: "distance", "time" or "velocity" + stepSize: 0.1 # step size, unit depends on stepVariable - position in meter, time in seconds and velocity in meter/second diff --git a/data/trains/train_freight_V90withOreConsist.yaml b/test/data/trains/freight.yaml similarity index 100% rename from data/trains/train_freight_V90withOreConsist.yaml rename to test/data/trains/freight.yaml diff --git a/data/trains/train_passenger_SiemensDesiroClassic.yaml b/test/data/trains/local.yaml similarity index 100% rename from data/trains/train_passenger_SiemensDesiroClassic.yaml rename to test/data/trains/local.yaml diff --git a/data/trains/train_passenger_IC2.yaml b/test/data/trains/longdistance.yaml similarity index 100% rename from data/trains/train_passenger_IC2.yaml rename to test/data/trains/longdistance.yaml diff --git a/test/runtests.jl b/test/runtests.jl index 5e98cc6..541b74b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,40 +1,69 @@ #!/usr/bin/env julia # -*- coding: UTF-8 -*- # __julia-version__ = 1.7.0 -# __author__ = "Max Kannenberg" +# __author__ = "Max Kannenberg, Martin Scheidt" # __copyright__ = "2021" # __license__ = "ISC" using TrainRun, Test -allPaths=[] -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")) +paths=Dict() +push!(paths, "const" => TrainRun.importFromYaml(:path, "test/data/paths/const.yaml")) +push!(paths, "slope" => TrainRun.importFromYaml(:path, "test/data/paths/slope.yaml")) +push!(paths, "speed" => TrainRun.importFromYaml(:path, "test/data/paths/speed.yaml")) +push!(paths, "realworld" => TrainRun.importFromYaml(:path, "test/data/paths/realworld.yaml")) +settings=Dict() +push!(settings, "default" => Settings()) +push!(settings, "detail" => Settings("test/data/settings/detail.yaml")) +push!(settings, "driving_course" => Settings("test/data/settings/driving_course.yaml")) +push!(settings, "strip" => Settings("test/data/settings/strip.yaml")) +push!(settings, "time" => Settings("test/data/settings/time.yaml")) +push!(settings, "time_strip" => Settings("test/data/settings/time_strip.yaml")) +push!(settings, "velocity" => Settings("test/data/settings/velocity.yaml")) +push!(settings, "csv_export" => Settings("test/data/settings/csv_export.yaml")) -allSettings=[] -push!(allSettings, importYamlFile(:settings, "data/settings.yaml")) +trains=Dict() +push!(trains, TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml")) +push!(trains, TrainRun.importFromYaml(:train, "test/data/trains/local.yaml")) +push!(trains, TrainRun.importFromYaml(:train, "test/data/trains/longdistance.yaml")) -allTrains=[] -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")) +@testset "TrainRun.jl" begin -for path in allPaths - for train in allTrains - for settings in allSettings - testDict=trainRun(train, path, settings) - exportToCsv(testDict) - sleep(2) + @testset "Default settings" begin + + @test typeof(Settings()) == Settings + + @testset "const path" begin + + path = TrainRun.importFromYaml(:path, "test/data/paths/const.yaml") + @test typeof(path) == Dict{Any,Any} + + @testset "freight train - const path" begin + train = TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml") + data = trainRun(train, path) + expected = 727.796900196972 + # compare result to test data set + @test isapprox(data, expected, atol=0.01) + end + + @testset "local train - const path" begin + train = TrainRun.importFromYaml(:train, "test/data/trains/local.yaml") + data = trainRun(train, path) + expected = 392.723361763612 + # compare result to test data set + @test isapprox(data, expected, atol=0.01) + end + + @testset "long distance train - const path" begin + train = TrainRun.importFromYaml(:train, "test/data/trains/longdistance.yaml") + data = trainRun(train, path) + expected = 328.83487704779117 + # compare result to test data set + @test isapprox(data, expected, atol=0.01) + end - # TODO: - # compare result to test data set end end -end -println("test finished") -# TODO: -# print test results +end diff --git a/test/testEnums.jl b/test/testEnums.jl deleted file mode 100644 index 9a0875c..0000000 --- a/test/testEnums.jl +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.0 -# __author__ = "Martin Scheidt" -# __copyright__ = "2021" -# __license__ = "ISC" - -include("../src/types.jl") -include("../src/Validate.jl") - -using .Input -using YAML, Test - -@enum trainType passenger=1 freight=2 motorCoachTrain=3 - -@test Input.getEnum("passenger", trainType) == passenger::trainType -@test Input.getEnum("freight", trainType) == freight::trainType -@test Input.getEnum("motorCoachTrain", trainType) == motorCoachTrain::trainType - -data = YAML.load(open("data/trains/train_passenger_IC2.yaml")) -@test Input.getEnum(data["train"]["trainType"], trainType) == passenger::trainType - -data = YAML.load(open("data/trains/train_freight_V90withOreConsist.yaml")) -@test Input.getEnum(data["train"]["trainType"], trainType) == freight::trainType