From e2842157dae9f8c4b60b8ed24328fcb0a19e67bd Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 28 Apr 2022 17:02:40 +0200 Subject: [PATCH 01/18] new type Settings as struct --- CHANGELOG.md | 18 + CONTRIBUTING.md | 9 + Project.toml | 14 +- README.md | 3 +- .../settings_distanceStep_homStrip.yaml | 12 - .../settings_distanceStep_massPoint.yaml | 12 - ...gs_distanceStep_massPoint_runningTime.yaml | 12 - data/settings/settings_timeStep_homStrip.yaml | 12 - .../settings/settings_timeStep_massPoint.yaml | 12 - .../settings_velocityStep_massPoint.yaml | 12 - docs/DEFAULT.yaml | 11 + docs/examples/ExtendedWorkingExample.jl | 32 + docs/examples/MinimalWorkingExample.jl | 10 + examples/ExtendedWorkingExample.jl | 44 - examples/MinimalWorkingExample.jl | 19 - src/AdditionalOutput.jl | 210 ---- src/Behavior.jl | 225 ++-- src/{TrainRunCalc.jl => Calc.jl} | 70 +- src/Characteristics.jl | 20 +- src/EnergySaving.jl | 1045 ----------------- src/Export.jl | 74 +- src/Formulary.jl | 44 +- src/Import.jl | 13 +- src/Output.jl | 18 +- src/TrainRun.jl | 59 +- src/{Validate.jl => Types.jl} | 174 +-- .../data/paths/const.yaml | 0 .../data/paths/realworld.yaml | 0 .../data/paths/slope.yaml | 0 .../data/paths/speed.yaml | 0 test/data/settings/csv_export.yaml | 5 + test/data/settings/detail.yaml | 9 + test/data/settings/driving_course.yaml | 4 + test/data/settings/strip.yaml | 4 + test/data/settings/time.yaml | 4 + test/data/settings/time_strip.yaml | 5 + test/data/settings/velocity.yaml | 5 + .../data/trains/freight.yaml | 0 .../data/trains/local.yaml | 0 .../data/trains/longdistance.yaml | 0 test/runtests.jl | 77 +- test/testEnums.jl | 24 - 42 files changed, 485 insertions(+), 1836 deletions(-) delete mode 100644 data/settings/settings_distanceStep_homStrip.yaml delete mode 100644 data/settings/settings_distanceStep_massPoint.yaml delete mode 100644 data/settings/settings_distanceStep_massPoint_runningTime.yaml delete mode 100644 data/settings/settings_timeStep_homStrip.yaml delete mode 100644 data/settings/settings_timeStep_massPoint.yaml delete mode 100644 data/settings/settings_velocityStep_massPoint.yaml create mode 100644 docs/DEFAULT.yaml create mode 100644 docs/examples/ExtendedWorkingExample.jl create mode 100644 docs/examples/MinimalWorkingExample.jl delete mode 100644 examples/ExtendedWorkingExample.jl delete mode 100644 examples/MinimalWorkingExample.jl delete mode 100644 src/AdditionalOutput.jl rename src/{TrainRunCalc.jl => Calc.jl} (75%) delete mode 100644 src/EnergySaving.jl rename src/{Validate.jl => Types.jl} (87%) rename data/paths/path_1_10km_nConst_vConst.yaml => test/data/paths/const.yaml (100%) rename data/paths/path_4_real_Germany_EastSaxony_DG-DN.yaml => test/data/paths/realworld.yaml (100%) rename data/paths/path_2_10km_nVar_vConst.yaml => test/data/paths/slope.yaml (100%) rename data/paths/path_3_10km_nConst_vVar.yaml => test/data/paths/speed.yaml (100%) create mode 100644 test/data/settings/csv_export.yaml create mode 100644 test/data/settings/detail.yaml create mode 100644 test/data/settings/driving_course.yaml create mode 100644 test/data/settings/strip.yaml create mode 100644 test/data/settings/time.yaml create mode 100644 test/data/settings/time_strip.yaml create mode 100644 test/data/settings/velocity.yaml rename data/trains/train_freight_V90withOreConsist.yaml => test/data/trains/freight.yaml (100%) rename data/trains/train_passenger_SiemensDesiroClassic.yaml => test/data/trains/local.yaml (100%) rename data/trains/train_passenger_IC2.yaml => test/data/trains/longdistance.yaml (100%) delete mode 100644 test/testEnums.jl 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 From 580e13f97b5b0d97fe85916c397a30e951535682 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 28 Apr 2022 17:23:23 +0200 Subject: [PATCH 02/18] added docs and GitHub actions from Julia template --- .github/workflows/CI.yml | 54 ++++++++++++++++++++++++++++++ .github/workflows/CompatHelper.yml | 16 +++++++++ .github/workflows/TagBot.yml | 15 +++++++++ .github/workflows/register.yml | 14 ++++++++ docs/Project.toml | 3 ++ docs/make.jl | 24 +++++++++++++ docs/src/index.md | 14 ++++++++ 7 files changed, 140 insertions(+) create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/CompatHelper.yml create mode 100644 .github/workflows/TagBot.yml create mode 100644 .github/workflows/register.yml create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/src/index.md diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..68202e0 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,54 @@ +name: CI +on: + push: + branches: + - master + tags: '*' + pull_request: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.7' + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-docdeploy@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + - run: | + julia --project=docs -e ' + using Documenter: DocMeta, doctest + using TrainRun + DocMeta.setdocmeta!(TrainRun, :DocTestSetup, :(using TrainRun); recursive=true) + doctest(TrainRun)' diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 0000000..cba9134 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,16 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} + run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml new file mode 100644 index 0000000..f49313b --- /dev/null +++ b/.github/workflows/TagBot.yml @@ -0,0 +1,15 @@ +name: TagBot +on: + issue_comment: + types: + - created + workflow_dispatch: +jobs: + TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' + runs-on: ubuntu-latest + steps: + - uses: JuliaRegistries/TagBot@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/register.yml b/.github/workflows/register.yml new file mode 100644 index 0000000..6e71f2f --- /dev/null +++ b/.github/workflows/register.yml @@ -0,0 +1,14 @@ +name: Register Package +on: + workflow_dispatch: + inputs: + version: + description: Version to register or component to bump + required: true +jobs: + register: + runs-on: ubuntu-latest + steps: + - uses: julia-actions/RegisterAction@latest + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..b513025 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +TrainRun = "e4541106-d44c-4e00-b50b-ecdf479fcf92" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..4b52efb --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,24 @@ +using TrainRun +using Documenter + +DocMeta.setdocmeta!(TrainRun, :DocTestSetup, :(using TrainRun); recursive=true) + +makedocs(; + modules=[TrainRun], + authors="Max Kannenberg, Martin Scheidt, and contributors", + repo="https://github.com/railtoolkit/TrainRun.jl/blob/{commit}{path}#{line}", + sitename="TrainRun.jl", + format=Documenter.HTML(; + prettyurls=get(ENV, "CI", "false") == "true", + canonical="https://railtoolkit.github.io/TrainRun.jl", + assets=String[], + ), + pages=[ + "Home" => "index.md", + ], +) + +deploydocs(; + repo="github.com/railtoolkit/TrainRun.jl", + devbranch="master", +) diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..217d810 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,14 @@ +```@meta +CurrentModule = TrainRun +``` + +# TrainRun + +Documentation for [TrainRun](https://github.com/railtoolkit/TrainRun.jl). + +```@index +``` + +```@autodocs +Modules = [TrainRun] +``` From 310cd013da8d9a831e5e8f9187512aaa0a30cf35 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 28 Apr 2022 17:29:24 +0200 Subject: [PATCH 03/18] changed upper case to lower case --- CHANGELOG.md | 2 +- src/behavior.jl | 1398 ++++++++++++++++++++++++++++++++++++++++ src/calc.jl | 160 +++++ src/characteristics.jl | 241 +++++++ src/export.jl | 212 ++++++ src/formulary.jl | 226 +++++++ src/import.jl | 35 + src/output.jl | 106 +++ src/types.jl | 799 +++++++++++++++++++++++ 9 files changed, 3178 insertions(+), 1 deletion(-) create mode 100644 src/behavior.jl create mode 100644 src/calc.jl create mode 100644 src/characteristics.jl create mode 100644 src/export.jl create mode 100644 src/formulary.jl create mode 100644 src/import.jl create mode 100644 src/output.jl create mode 100644 src/types.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index a9b7176..37241e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * 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 tilte of include files from upper case to lower case * changed seperation of submodules into a single module with file include ### Removed diff --git a/src/behavior.jl b/src/behavior.jl new file mode 100644 index 0000000..d1815d2 --- /dev/null +++ b/src/behavior.jl @@ -0,0 +1,1398 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +## functions for calculating tractive effort and resisting forces +""" + calculateTractiveEffort(v, tractiveEffortVelocityPairs) + +Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` dependend on the velocity `v`. + +... +# Arguments +- `v::AbstractFloat`: the current velocity in m/s. +- `tractiveEffortVelocityPairs::Array{Array{AbstractFloat,1},1}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. +... + +# Examples +```julia-repl +julia> calculateTractiveEffort(20.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) +100000 + +julia> calculateTractiveEffort(30.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) +80000 +``` +""" +function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs) + for row in 1:length(tractiveEffortVelocityPairs) + nextPair = tractiveEffortVelocityPairs[row] + if nextPair[1] == v + return nextPair[2] + elseif nextPair[1] > v + # interpolate for a straight line between the two surrounding points with the formula: F=(v-v_(row-1))*(F_row-F_(row-1))/(v_row-v_(row-1))+F_(row-1) + previousPair = tractiveEffortVelocityPairs[row-1] + F_T_interpolation = (v-previousPair[1]) * (nextPair[2]-previousPair[2]) / (nextPair[1]-previousPair[1]) + previousPair[2] + return F_T_interpolation + end #if + end #for + # if v gets higher than the velocities in tractiveEffortVelocityPairs the last tractive effort will be used + # TODO: also an extrapolation could be used + return tractiveEffortVelocityPairs[end][2] +end #function calculateTractiveEffort + +""" +calculate and return the path resistance dependend on the trains position and mass model +""" +function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) + + if massModel == :mass_point + pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) + 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] + pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train[:length] * calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) + csId = csId-1 + if csId == 0 + # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used + return pathResistance + (CSs[1][:s_entry] - s_rear) / train[:length] * calcForceFromCoefficient(CSs[1][:r_path], train[:m_train]) + end #if + end #while + end #if + + return pathResistance +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) + # calculate resisting forces + dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) + dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) + dataPoint[:R_train] = dataPoint[:R_traction] + dataPoint[:R_wagons] + dataPoint[:R_path] = calculatePathResistance(CSs, csId, dataPoint[:s], massModel, train) + dataPoint[:F_R] = dataPoint[:R_train] + dataPoint[:R_path] + + # calculate tractive effort + if bsType == "braking" || bsType == "coasting" + dataPoint[:F_T] = 0.0 + elseif bsType == "cruising" + dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])) + else # bsType == "accelerating" || bsType == "diminishing" || 'default' + dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs]) + end + + return dataPoint +end #function calculateForces! + + +""" +TODO +""" +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. =# + + # create the next data point + newPoint = createDataPoint() + newPoint[:i] = previousPoint[:i]+1 # identifier + + # calculate s, t, v, E + if stepVariable == :distance # distance step method + newPoint[:Δs] = stepSize # step size (in m) + 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,".") + end + newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) + newPoint[:Δv] = 0.0 # step size (in m/s) + else + # check if the parts of the following square roots will be <0.0 in the functions calc_Δt_with_Δs and calc_Δv_with_Δs + squareRootPartIsNegative = (previousPoint[:v]/previousPoint[:a])^2+2*newPoint[:Δs]/previousPoint[:a] < 0.0 || previousPoint[:v]^2+2*newPoint[:Δs]*previousPoint[:a] < 0.0 + if previousPoint[:a] < 0.0 && squareRootPartIsNegative + error("ERROR: The train stops during the accelerating section in CS",csId," because the tractive effort is lower than the resistant forces.", + " Before the stop the last point has the values s=",previousPoint[:s]," m, v=",previousPoint[:v]," m/s, a=",previousPoint[:a]," m/s^2,", + " F_T=",previousPoint[:F_T]," N, R_traction=",previousPoint[:R_traction]," N, R_wagons=",previousPoint[:R_wagons]," N, R_path=",previousPoint[:R_path]," N.") + end + newPoint[:Δt] = calc_Δt_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in s) + newPoint[:Δv] = calc_Δv_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in m/s) + end + + 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 == :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,".") + end + newPoint[:Δs] = stepSize # step size (in m) + # TODO what is the best default step size for constant v? define Δs or Δt? + newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) + newPoint[:Δv] = 0.0 # step size (in m/s) + else + newPoint[:Δv] = stepSize * sign(previousPoint[:a]) # step size (in m/s) + newPoint[:Δs] = calc_Δs_with_Δv(newPoint[:Δv], previousPoint[:a], previousPoint[:v]) # step size (in m) + newPoint[:Δt] = calc_Δt_with_Δv(newPoint[:Δv], previousPoint[:a]) # step size (in s) + end + end #if + + newPoint[:s] = previousPoint[:s] + newPoint[:Δs] # position (in m) + newPoint[:t] = previousPoint[:t] + newPoint[:Δt] # point in time (in s) + newPoint[:v] = previousPoint[:v] + newPoint[:Δv] # velocity (in m/s) + newPoint[:ΔW] = calc_ΔW(previousPoint[:F_T], newPoint[:Δs]) # mechanical work in this step (in Ws) + newPoint[:W] = previousPoint[:W] + newPoint[:ΔW] # mechanical work (in Ws) + newPoint[:ΔE] = calc_ΔE(newPoint[:ΔW]) # energy consumption in this step (in Ws) + newPoint[:E] = previousPoint[:E] + newPoint[:ΔE] # energy consumption (in Ws) + + + return newPoint +end #function moveAStep + +""" +# 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 +""" +function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::Real, trainLength::Real) + v_limit = CSs[csWithTrainHeadId][:v_limit] + s_exit = CSs[csWithTrainHeadId][:s_exit] + if csWithTrainHeadId > 1 && s -trainLength < CSs[csWithTrainHeadId][:s_entry] + formerCsId = csWithTrainHeadId-1 + while formerCsId > 0 && s -trainLength < CSs[formerCsId][:s_exit] + if CSs[formerCsId][:v_limit] < v_limit # TODO: is the position of the train's rear < movingSection[:s_entry], v_limit of the first CS is used + v_limit = CSs[formerCsId][:v_limit] + s_exit = CSs[formerCsId][:s_exit] + end + formerCsId = formerCsId -1 + end + end + currentSpeedLimit = Dict(:v => v_limit, :s_end => s_exit + trainLength) + return currentSpeedLimit +end #function getCurrentSpeedLimit + +function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real) + for s_POI in pointsOfInterest + if s_POI > s + return s_POI + end + end + error("ERROR in getNextPointOfInterest: There is no POI higher than s=",s," m.") +end #function getNextPointOfInterest + +## This function calculates the data points of the breakFree section. +# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. +# Info: currently the values of the breakFree section will be calculated like in the accelerating section +function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + # conditions for the break free section + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] + trainIsHalting = drivingCourse[end][:v] == 0.0 + + if trainIsHalting && !endOfCSReached + BS = createBehaviorSection("breakFree", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + 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 + + # 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) + catch(acceleratingError) + println("This error happened during the break free phase that is using the accelerating function:") + rethrow(acceleratingError) + end + + # delete every dataPoint except the first two + while drivingCourse[end][:i] > drivingCourse[BS[:dataPoints][1]][:i] +1 + pop!(drivingCourse) + end + + # change the accelerating data to break free + drivingCourse[end-1][:behavior] = BS[:type] + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + + # remove the accelerating section from the CS + CS[:t] = CS[:t] - get(CS[:behaviorSections], :accelerating, Dict(:t=>0.0))[:t] # total running time (in s) + CS[:E] = CS[:E] - get(CS[:behaviorSections], :accelerating, Dict(:E=>0.0))[:E] # total energy consumption (in Ws) + delete!(CS[:behaviorSections], :accelerating) + + # calculate the accumulated breakFree section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + merge!(CS[:behaviorSections], Dict(:breakFree => BS)) + end # else: return the characteristic section without a breakFree section + + # determine state flags + + if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] + s_braking = 0.0 + else + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + + # reset state flags + stateFlags[:endOfCSReached] = drivingCourse[end][:s] >= CS[:s_exit] + stateFlags[:brakingStartReached] = drivingCourse[end][:s] +s_braking >= CS[:s_exit] + stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] # or add another flag for equal forces? + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 + stateFlags[:previousSpeedLimitReached] = false + stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_limit] + stateFlags[:error] = !(stateFlags[:endOfCSReached] || stateFlags[:brakingStartReached] || stateFlags[:tractionDeficit] || stateFlags[:previousSpeedLimitReached] || stateFlags[:speedLimitReached]) + + return (CS, drivingCourse, stateFlags) +end #function addBreakFreeSection! + +## This function calculates the data points of the clearing section. + # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. +function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + if stateFlags[:previousSpeedLimitReached] + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + + if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] + ignoreBraking = true + s_braking = 0.0 + else + ignoreBraking = false + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + + 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) + # stateFlags[:brakingStartReached] = brakingStartReached + # stateFlags[:endOfCSReached] = stateFlags[:endOfCSReached] || drivingCourse[end][:s] == CS[:s_exit] + else + error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id]) + end + #stateFlags[:previousSpeedLimitReached] = false + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] + else + stateFlags[:error] = true + end + + return (CS, drivingCourse, stateFlags) +end #function addClearingSection + +## This function calculates the data points of the accelerating section. + # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section +function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) + #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) + CSs = movingSection[:characteristicSections] + CS = CSs[csId] + drivingCourse = movingSection[:drivingCourse]=# + + calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) + + if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] + ignoreBraking = true + s_braking = 0.0 + else + ignoreBraking = false + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + + # conditions for the accelerating section + targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] || stateFlags[:speedLimitReached] + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] + tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] + brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] + previousSpeedLimitReached = stateFlags[:previousSpeedLimitReached] + + # use the conditions for the accelerating section + if !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached + BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] + + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] + speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit] + #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 + nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + + 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 + + while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached + # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit + if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] + # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + 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])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) + + # conditions for the next while cycle + if !ignoreBraking + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] + speedLimitReached = drivingCourse[end][:v] > CS[:v_limit] + previousSpeedLimitReached = currentSpeedLimit[:v] < CS[:v_limit] && (drivingCourse[end][:v] > currentSpeedLimit[:v] || (drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end])) + targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] + #targetSpeedReached = speedLimitReached + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well + tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] + end #while + + if CS[:id]==0 + testFlag = true + else + testFlag = false # for testing + end + + # check which limit was reached and adjust the currentStepSize for the next cycle + 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 + + 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 + + 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 == :distance + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + else + 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 == :speed + currentStepSize = CS[:v_peak]-drivingCourse[end-1][:v] + else + 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 == :velocity + currentStepSize = currentSpeedLimit[:v]-drivingCourse[end-1][:v] + + else + currentStepSize = settings.stepSize / 10.0^cycle + end + + 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 + end + break + + 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 + break + + elseif drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing + break + + elseif drivingCourse[end][:s] == nextPointOfInterest + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing + if nextPointOfInterest == CS[:s_exit] + endOfCSReached = true + end + break + + else + println("v=",drivingCourse[end][:v]," v_peak= ", CS[:v_peak] , " v_cLimit=", currentSpeedLimit[:v]) + 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 CS[:v_peak] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=",CS[:v_peak]) # for testing + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next section + brakingStartReached = false + + 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 + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + else + drivingCourse[end][:s] = CS[:s_exit] # round s down to CS[:s_exit] + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + end + + elseif drivingCourse[end][:s] > nextPointOfInterest + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing + drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + + elseif 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 + + 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 + previousSpeedLimitReached = true + + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + else + if drivingCourse[end][:s] + s_braking == CS[:s_exit] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," else case and there: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing + 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 + + end + end + + # TODO is it possible to put this into to the if-fork? + if drivingCourse[end][:s] == CS[:s_exit] + endOfCSReached = true + end + + end + end #for + + if drivingCourse[end][:s] == CS[:s_exit] + endOfCSReached = true + end + + end #while + + if length(BS[:dataPoints]) > 1 + # calculate the accumulated accelerating section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + # 03/10 old: CS[:v_peak] = max(drivingCourse[end][:v], CS[:v_entry]) # setting v_peak to the last data points velocity which is the highest reachable value in this characteristic section or to v_entry in case it is higher when running on a path with high resistances + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + mergeBehaviorSection!(CS[:behaviorSections], BS) + end + end + + # set state flags + stateFlags[:endOfCSReached] = endOfCSReached + stateFlags[:brakingStartReached] = brakingStartReached + stateFlags[:tractionDeficit] = !(tractionSurplus || drivingCourse[end][:F_T] == drivingCourse[end][:F_R]) # or add another flag for equal forces? + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 + stateFlags[:previousSpeedLimitReached] = previousSpeedLimitReached + stateFlags[:speedLimitReached] = targetSpeedReached + stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) + + return (CS, drivingCourse, stateFlags) +end #function addAcceleratingSection! + + +## This function calculates the data points of the cruising section. +# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed. +function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Dict, CSs::Vector{Dict}, cruisingType::String) + trainIsClearing = cruisingType == "clearing" + trainIsBrakingDownhill = cruisingType == "downhillBraking" + + # traction effort and resisting forces (in N) + if !trainIsBrakingDownhill # TODO: or just give BS[:type] instead of "cruising"/"braking"? + calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) + else + calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) + end + + if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] + ignoreBraking = true + s_braking = 0.0 + else + ignoreBraking = false + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + + # conditions for cruising section + #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] + speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + targetPositionReached = s_cruising == 0.0 + resistingForceNegative = drivingCourse[end][:F_R] < 0 +#println(" vor if speedIsValid=",speedIsValid ," brakingStartReached=", brakingStartReached," tractionDeficit=", tractionDeficit," targetPositionReached=", targetPositionReached) + + if speedIsValid && !brakingStartReached && !tractionDeficit && !targetPositionReached + # 03/04 old: if drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] && !brakingStartReached && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] + BS = createBehaviorSection(cruisingType, drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] + # TODO: necessary? + s_cruising = min(s_cruising, CS[:s_exit]-BS[:s_entry]) + + # traction effort and resisting forces (in N) +#03/25 calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) + if !trainIsBrakingDownhill + calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) + else + calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) + end + + 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 + resistingForceNegative = drivingCourse[end][:F_R] < 0.0 +# targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak] + # TODO: change? to correctCruisingType = (trainIsClearing || (trainIsBrakingDownhill == drivingCourse[end][:F_R] < 0)) # while clearing tractive or braking force can be used +#&& 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 + nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + + 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 + #TODO: maybe just consider former CS with different path resistance? + # tractive effort (in N): +#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) + if !trainIsBrakingDownhill + drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) + else + drivingCourse[end][:F_T] = 0.0 + end + + + # acceleration (in m/s^2): + drivingCourse[end][:a] = 0.0 + + # create the next data point + if settings.stepVariable == :distance || settings.stepVariable == time + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) + else + push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? + 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) + #if !trainIsBrakingDownhill + # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) + #else + # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) + #end + + # conditions for the next while cycle + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising + trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] + resistingForceNegative = drivingCourse[end][:F_R] < 0.0 + end #while + + # check which limit was reached and adjust the currentStepSize for the next cycle + if cycle < settings.approxLevel+1 + if drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + currentStepSize = settings.stepSize / 10.0^cycle + + elseif !trainIsBrakingDownhill && resistingForceNegative + currentStepSize = settings.stepSize / 10.0^cycle + + elseif trainIsBrakingDownhill && !resistingForceNegative + currentStepSize = settings.stepSize / 10.0^cycle + + elseif drivingCourse[end][:s] > nextPointOfInterest + if settings.stepVariable == :distance + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + else + 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 == :distance + currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] + else + currentStepSize = settings.stepSize / 10.0^cycle + end + + elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] + break + + elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] + break + + elseif drivingCourse[end][:s] == nextPointOfInterest + break + + elseif !trainInPreviousCS + 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") + end + + # delete last data point for recalculating the last step with reduced step size + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next for cycle + pointOfInterestReached = false + tractionDeficit = false + targetPositionReached = false + trainInPreviousCS = true + resistingForceNegative = drivingCourse[end][:F_R] < 0.0 + + else # if the level of approximation is reached + if drivingCourse[end][:s] > nextPointOfInterest + drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + elseif drivingCourse[end][:s] > BS[:s_entry]+s_cruising + if BS[:type] != "clearing" + pop!(drivingCourse) + pop!(BS[:dataPoints]) + end + elseif drivingCourse[end][:s] == BS[:s_entry]+s_cruising + break + elseif drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + break + elseif !trainIsBrakingDownhill && resistingForceNegative + break + elseif trainIsBrakingDownhill && !resistingForceNegative + break + elseif !trainInPreviousCS + break + + else + + end + end + end #for + end #while + end #if + + # conditions for the next while cycle + targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + resistingForceNegative = drivingCourse[end][:F_R] < 0.0 + + while !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used + # 03/09 old: while drivingCourse[end][:s] < BS[:s_entry]+s_cruising && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] + nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])) + + # tractive effort (in N): +#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) + if !trainIsBrakingDownhill + drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) + else + drivingCourse[end][:F_T] = 0.0 + end + + + + drivingCourse[end][:a] = 0.0 # acceleration (in m/s^2) + + # calculate the remaining cruising way + #s_cruisingRemaining=BS[:s_entry] + s_cruising-drivingCourse[end][:s] + 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], :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) + #if !trainIsBrakingDownhill + # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) + #else + # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) + #end + + # conditions for the next while cycle + targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + resistingForceNegative = drivingCourse[end][:F_R] < 0 + end #while + + # TODO: realize this better inside the upper loops? + + + # calculate the accumulated cruising section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + mergeBehaviorSection!(CS[:behaviorSections], BS) + end # else: return the characteristic section without a cruising section + + # set state flags + stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit] + if !ignoreBraking + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit] + stateFlags[:tractionDeficit] = tractionDeficit + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0 + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] + stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative))) + + return (CS, drivingCourse, stateFlags) +end #function addCruisingSection! + + +## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower +function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) + + if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] + ignoreBraking = true + s_braking = 0.0 + else + ignoreBraking = false + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + + # conditions for diminishing section + targetSpeedReached = drivingCourse[end][:v] <= 0.0 + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit] + #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] + + # use the conditions for the diminishing section + if tractionDeficit && !targetSpeedReached && !endOfCSReached + BS = createBehaviorSection("diminishing", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] + + while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached + 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: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])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + + calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) + + # conditions for the next while cycle + if !ignoreBraking + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + end + brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + targetSpeedReached = drivingCourse[end][:v] <= 0.0 + tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + endOfCSReached = drivingCourse[end][:s] == CS[:s_exit] + end #while + + if CS[:id]==0 + testFlag = true + else + testFlag = false # for testing + end + + # check which limit was reached and adjust the currentStepSize for the next cycle + if cycle < settings.approxLevel+1 + if drivingCourse[end][:v] < 0.0 + if settings.stepVariable == velocity + currentStepSize = drivingCourse[end-1][:v] + else + 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 + + 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 + + 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 == :distance + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + else + currentStepSize = settings.stepSize / 10.0^cycle + end + + elseif 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 + break + + elseif drivingCourse[end][:s] == nextPointOfInterest + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing + break + + 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 + break + + elseif drivingCourse[end][:v] == 0.0 + error("ERROR: The train stops during diminishing run in CS",CS[:id]," at position s=",drivingCourse[end][:s]," m because the maximum tractive effort is lower than the resistant forces.", + " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", + " 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") + end + # delete last data point for recalculating the last step with reduced step size + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next for cycle + brakingStartReached = false + pointOfInterestReached = false + targetSpeedReached = false + tractionDeficit = true + endOfCSReached = false + + else # if the level of approximation is reached + if drivingCourse[end][:v] <= 0.0 + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: v=", drivingCourse[end][:v]," <= 0.0") # for testing + # push!(BS[:dataPoints], drivingCourse[end][:i]) + error("ERROR: The train stops during diminishing run in CS",CS[:id]," because the maximum tractive effort is lower than the resistant forces.", + " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", + " 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.") + + 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 + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + pointOfInterestReached = false + targetSpeedReached = false + tractionDeficit = true + endOfCSReached = false + + elseif drivingCourse[end][:s] > nextPointOfInterest + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing + drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + + 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 + break + + else + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: else with v=", drivingCourse[end][:v]," > 0.0 and F_T=", drivingCourse[end][:F_T]," <= F_R=", drivingCourse[end][:F_R]) # for testing + #println(" and s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," <= s_exit=",CS[:s_exit]) # for testing + #println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest=",nextPointOfInterest) # for testing + + # if drivingCourse[end][:s] + s_braking == CS[:s_exit] + # brakingStartReached = true + # end + end #if + + # # TODO is it possible to put this into to the if-fork? + # if drivingCourse[end][:s] == CS[:s_exit] + # endOfCSReached = true + # end + end #if + end #for + end #while + + if length(BS[:dataPoints]) > 1 # TODO: necessary? May it be possible that there is no diminishing because braking has to start? + # calculate the accumulated diminishing section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + mergeBehaviorSection!(CS[:behaviorSections], BS) + end + end + + # set state flags + stateFlags[:endOfCSReached] = endOfCSReached + stateFlags[:brakingStartReached] = brakingStartReached + stateFlags[:tractionDeficit] = tractionDeficit + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 + stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_peak] + stateFlags[:error] = !(endOfCSReached || brakingStartReached || !tractionDeficit) + + return (CS, drivingCourse, stateFlags) +end #function addDiminishingSection! + + +## This function calculates the data points of the coasting section. +# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section +function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + # 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 + + # conditions for coasting section + targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] + + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] + + # use the conditions for the coasting section + if !targetSpeedReached && !endOfCSReached + BS = createBehaviorSection("coasting", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] + + while !targetSpeedReached && !endOfCSReached && !brakingStartReached + 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: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) + + # 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])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + + # conditions for the next while cycle + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] + end # while + + testFlag = false + + # check which limit was reached and adjust the currentStepSize for the next cycle + 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 + + 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 == :distance + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + else + 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 == velocity + currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] + else + 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 == velocity + currentStepSize = CS[:v_peak] - drivingCourse[end-1][:v] + else + 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 + break + + elseif drivingCourse[end][:v] == CS[:v_exit] + testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," == v_exit=", CS[:v_exit]) # for testing + break + + elseif drivingCourse[end][:s] == nextPointOfInterest + testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing + break + + 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] + # delete last data point because it went to far + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next for cycle + # brakingStartReached = true + pointOfInterestReached = false + targetSpeedReached = false + + elseif drivingCourse[end][:v] > CS[:v_peak] # if the train gets to fast it has to brake # TODO: if accelereation and coasting functions will be combined this case is different for coasting and also the order of if cases is different + # delete last data point because it went to far + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next for cycle + brakingStartReached = false + pointOfInterestReached = false + # targetSpeedReached = true + + elseif drivingCourse[end][:s] > nextPointOfInterest + drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + else + # do nothing for example for drivingCourse[end][:s] + s_braking == CS[:s_exit] + end + end + end #for + end #while + + stateFlags[:speedLimitReached] = false + + # calculate the accumulated coasting section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + merge!(CS[:behaviorSections], Dict(:coasting=>BS)) + end + + # set state flags + stateFlags[:endOfCSReached] = endOfCSReached + stateFlags[:brakingStartReached] = brakingStartReached + stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 + stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) + + return (CS, drivingCourse, stateFlags) +end #function addCoastingSection! + + +## This function calculates the data points of the braking section. +# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. +function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) + # conditions for braking section + targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] + + # use the conditions for the braking section + if !targetSpeedReached && !endOfCSReached + BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + drivingCourse[end][:behavior] = BS[:type] + + while !targetSpeedReached && !endOfCSReached + 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: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) + + # 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 == :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 + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + else + # create the next data point + push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) + drivingCourse[end][:behavior] = BS[:type] + push!(BS[:dataPoints], drivingCourse[end][:i]) + end + #println(drivingCourse[end][:i],". s=",drivingCourse[end][:s]," s_exit=", CS[:s_exit]," v_exit=", CS[:v_exit]," v=",drivingCourse[end][:v]) + + # conditions for the next while cycle + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] + targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] + end # while + + # 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 < settings.approxLevel+1 + if drivingCourse[end][:v] < CS[:v_exit] + if settings.stepVariable == velocity + currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] + else + currentStepSize = settings.stepSize / 10.0^cycle + end + elseif drivingCourse[end][:s] > nextPointOfInterest + if settings.stepVariable == :distance + currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + else + currentStepSize = settings.stepSize / 10.0^cycle + end + elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] + break + elseif drivingCourse[end][:v] == CS[:v_exit] + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + endOfCSReached = true + # println(" with a=", drivingCourse[end-1][:a]) # for testing + break + elseif drivingCourse[end][:s] == CS[:s_exit] + # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + targetSpeedReached = true + # println(" with a=", drivingCourse[end-1][:a]) # for testing + break + elseif drivingCourse[end][:s] == nextPointOfInterest + break + end + + # delete last data point for recalculating the last step with reduced step size + pop!(drivingCourse) + pop!(BS[:dataPoints]) + + # conditions for the next for cycle + pointOfInterestReached = false + endOfCSReached = false + targetSpeedReached = false + + else # if the level of approximation is reached + if drivingCourse[end][:v] < 0.0 + # TODO: drivingCourse[end][:v] < CS[:v_exit] should be enough + # reset last point with setting v=v_exit + # println("during braking section in CS",CS[:id],": rounding v up from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], 0.0) + endOfCSReached = true + break + elseif drivingCourse[end][:s] > CS[:s_exit] + # println("during braking section in CS",CS[:id],": rounding s down from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing + # recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + drivingCourse[end][:s] = CS[:s_exit] + break + elseif drivingCourse[end][:s] > nextPointOfInterest + drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] + break + elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] + break + elseif drivingCourse[end][:v] < CS[:v_exit] + # reset last point with setting v=v_exit + # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + endOfCSReached = true + break + elseif drivingCourse[end][:v] == CS[:v_exit] + # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + endOfCSReached = true + break + elseif drivingCourse[end][:s] == CS[:s_exit] + # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing + recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) + targetSpeedReached = true + break + else + # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest + end + end + end #for + end #while + + # calculate the accumulated coasting section information + merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) + :s_exit => drivingCourse[end][:s], # last position (in m) + :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) + :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + + CS[:t] = CS[:t] + BS[:t] # total running time (in s) + CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) + + merge!(CS[:behaviorSections], Dict(:braking=>BS)) + end # else: return the characteristic section without a braking section + + # set state flags + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] + stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] + stateFlags[:endOfCSReached] = endOfCSReached + stateFlags[:error] = !(endOfCSReached) + calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) + stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 + + return (CS, drivingCourse, stateFlags) +end #function addBrakingSection! + + +## This function calculates the data point of the standstill. +# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. +function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) + 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) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :s_exit => drivingCourse[end][:s], # last position (in m) + :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) + drivingCourse[end][:behavior] = BS[:type] + + # traction effort and resisting forces (in N) + 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 + return (CS, drivingCourse) +end #function addStandstill! + +function mergeBehaviorSection!(BSs::Dict, BS::Dict) + if !haskey(BSs, Symbol(BS[:type])) + merge!(BSs, Dict(Symbol(BS[:type]) => BS)) + else + number = "2" + while haskey(BSs, Symbol(BS[:type]*number)) + number = string(parse(Int, number)+1) + end + merge!(BSs, Dict(Symbol(BS[:type]*number) => BS)) + # println("INFO: The ",number,". ",BS[:type]," section has been created. ! ! ! ! ! ! ! ! !") + end + return BSs +end #function mergeBehaviorSection! + +function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) + BS= Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill + :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" + :length => 0.0, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => 0.0, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_entry => v_entry, # entry speed (in m/s) + :v_exit => 0.0, # exit speed (in m/s) + :dataPoints => [startingPoint]) # list of identifiers of the containing data points starting with the initial point + return BS +end #function createBehaviorSection + +""" +a data point is the smallest element of the driving course. One step of the step approach is between two data points +""" +function createDataPoint() + dataPoint = Dict(:i => 0, # identifier and counter variable of the dricing course + :behavior => "", # type of behavior section the data point is part of ("breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") + # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter + :s => 0.0, # position (in m) + :Δs => 0.0, # step size (in m) + :t => 0.0, # point in time (in s) + :Δt => 0.0, # step size (in s) + :v => 0.0, # velocity (in m/s) + :Δv => 0.0, # step size (in m/s) + :a => 0.0, # acceleration (in m/s^2) + :W => 0.0, # mechanical work (in Ws) + :ΔW => 0.0, # mechanical work in this step (in Ws) + :E => 0.0, # energy consumption (in Ws) + :ΔE => 0.0, # energy consumption in this step (in Ws) + :F_T => 0.0, # tractive effort (in N) + :F_R => 0.0, # resisting force (in N) + :R_path => 0.0, # path resistance (in N) + :R_train => 0.0, # train resistance (in N) + :R_traction => 0.0, # traction unit resistance (in N) + :R_wagons => 0.0) # set of wagons resistance (in N) + return dataPoint +end #function createDataPoint + +function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) + currentPoint = drivingCourse[end] + previousPoint = drivingCourse[end-1] + # set s and v + currentPoint[:s] = s_target # position (in m) + currentPoint[:v] = v_target # velocity (in m/s) + currentPoint[:Δs] = currentPoint[:s] - previousPoint[:s] # step size (in m) + currentPoint[:Δv] = currentPoint[:v] - previousPoint[:v] # step size (in m/s) + + # calculate other values + previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs]) +# # TODO: just for testing +# if previousPoint[:a]=0.0 +# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train[:a_braking]) +# end + currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s) + currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s) + + currentPoint[:ΔW] = 0.0 # mechanical work in this step (in Ws) + currentPoint[:W] = previousPoint[:W] + currentPoint[:ΔW] # mechanical work (in Ws) + currentPoint[:ΔE] = currentPoint[:ΔW] # energy consumption in this step (in Ws) + currentPoint[:E] = previousPoint[:E] + currentPoint[:ΔE] # energy consumption (in Ws) +end #function recalculateLastBrakingPoint diff --git a/src/calc.jl b/src/calc.jl new file mode 100644 index 0000000..3ba2662 --- /dev/null +++ b/src/calc.jl @@ -0,0 +1,160 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +# 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::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) +todo !!! +``` +""" +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.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) + train = copy(trainInput) + path = copy(pathInput) + + # check the input data + (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.outputDetail == :everything && println("The moving section has been prepared.") + + # calculate the train run for oparation mode "minimum running time" + (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) + + return output +end # function trainRun + +# calculate a train run focussing on using the minimum possible running time +function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) + CSs::Vector{Dict} = movingSection[:characteristicSections] + + 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) + drivingCourse::Vector{Dict} = [startingPoint] # List of data points + + for csId in 1:length(CSs) + CS = CSs[csId] + # for testing + if drivingCourse[end][:s] != CS[:s_entry] + println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry]) + end + if drivingCourse[end][:v] > CS[:v_entry] + println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry]) + end + + # determine the different flags for switching between the states for creatinge moving phases + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + 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], + :brakingStartReached => drivingCourse[end][:s] + s_braking == CS[:s_exit], + :tractionDeficit => drivingCourse[end][:F_T] < drivingCourse[end][:F_R], # or add another flag for equal forces? + :resistingForceNegative => drivingCourse[end][:F_R] < 0.0, + :previousSpeedLimitReached => false, #speedLimitReached, # check already at this position? + :speedLimitReached => drivingCourse[end][:v] > CS[:v_limit], + :error => false) + + # determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") + while !stateFlags[:endOfCSReached] # s < s_exit + if !stateFlags[:brakingStartReached] # s+s_braking < s_exit + if !stateFlags[:tractionDeficit] + if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0 + (CS, drivingCourse, stateFlags) = addBreakFreeSection!(CS, drivingCourse, stateFlags, settings, train, CSs) + + elseif stateFlags[:previousSpeedLimitReached] + (CS, drivingCourse, stateFlags) = addClearingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) + + elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] + (CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) + + elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] + # cruise only one step + 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") + + elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached] + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking + + if s_cruising > 0.0 + (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") + else + stateFlags[:brakingStartReached] = true + end + + elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached] + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking + + if s_cruising > 0.0 # TODO: define a minimum cruising length? + (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") + else + stateFlags[:brakingStartReached] = true + end + else + error() + end + elseif stateFlags[:tractionDeficit] + (CS, drivingCourse, stateFlags) = addDiminishingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) + + else + error() + end + else#if !stateFlags[:endOfCSReached] # s < s_exit + (CS, drivingCourse, stateFlags) = addBrakingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) + #else + # error() + end + end + #if s == s_exit + # standstill + #end + + + # for testing: + if drivingCourse[end][:s] != CS[:s_exit] + println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit]) + end + if drivingCourse[end][:v] > CS[:v_exit] + println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit]) + end + end #for + + (CSs[end], drivingCourse) = addStandstill!(CSs[end], drivingCourse, settings, train, CSs) + + movingSection[:t] = drivingCourse[end][:t] # total running time (in s) + movingSection[:E] = drivingCourse[end][:E] # total energy consumption (in Ws) + + return (movingSection, drivingCourse) +end #function calculateMinimumRunningTime diff --git a/src/characteristics.jl b/src/characteristics.jl new file mode 100644 index 0000000..4dd83c2 --- /dev/null +++ b/src/characteristics.jl @@ -0,0 +1,241 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior +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) + #movingSection = secureCruisingBehavior!(movingSection, settings, train) + + return movingSection +end #function determineCharacteristics + +## create a moving section containing characteristic sections +function createMovingSection(path::Dict, v_trainLimit::Real) + # this function creates and returns a moving section dependent on the paths attributes + + s_entry = path[:sections][1][:s_start] # first position (in m) + s_exit = path[:sections][end][:s_end] # last position (in m) + pathLength = s_exit - s_entry # total length (in m) + + CSs=Vector{Dict}() + s_csStart=s_entry + csId=1 + for row in 2:length(path[:sections]) + previousSection = path[:sections][row-1] + currentSection = path[:sections][row] + speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) + pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] + if speedLimitIsDifferent || pathResistanceIsDifferent + # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] + push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), path)) + s_csStart = currentSection[:s_start] + csId = csId+1 + end #if + end #for + push!(CSs, createCharacteristicSection(csId, s_csStart, path[:sections][end], min(path[:sections][end][:v_limit], v_trainLimit), path)) + + movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore + :length => pathLength, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => s_exit, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :characteristicSections => CSs) # list of containing characteristic sections + + return movingSection +end #function createMovingSection + + +## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. +function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, path::Dict) + # Create and return a characteristic section dependent on the paths attributes + characteristicSection= Dict(:id => id, # identifier + :s_entry => s_entry, # first position (in m) + :s_exit => section[:s_end], # last position (in m) + :length => section[:s_end] -s_entry, # total length (in m) + :r_path => section[:f_Rp], # path resistance (in ‰) + :behaviorSections => Dict(), # list of containing behavior sections + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_limit => v_limit, # speed limit (in m/s) + # initializing :v_entry, :v_peak and :v_exit with :v_limit + :v_peak => v_limit, # maximum reachable speed (in m/s) + :v_entry => v_limit, # maximum entry speed (in m/s) + :v_exit => v_limit) # maximum exit speed (in m/s) + + # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated + s_exit = characteristicSection[:s_exit] + pointsOfInterest = Vector{Real}() + if haskey(path, :pointsOfInterest) + for POI in path[:pointsOfInterest] + if s_entry < POI && POI < s_exit + push!(pointsOfInterest, POI) + end + end + end + push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with + + merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) + + return characteristicSection +end #function createCharacteristicSection + +## define the intersection velocities between the characterisitc sections to secure braking behavior +function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) + # this function limits the entry and exit velocity of the characteristic sections to secure that the train stops at the moving sections end + CSs = movingSection[:characteristicSections] + + csId = length(CSs) + followingCSv_entry = 0.0 # the exit velocity of the last characteristic section is 0.0 m/s + while csId >= 1 + CS = CSs[csId] + + CS[:v_exit] = min(CS[:v_limit], followingCSv_entry) + + v_entryMax = calcBrakingStartVelocity(CS[:v_exit], a_braking, CS[:length]) + + CS[:v_entry] = min(CS[:v_limit], v_entryMax) + CS[:v_peak] = CS[:v_entry] + + + # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit + CS[:behaviorSections] = Dict() + CS[:E] = 0.0 + CS[:t] = 0.0 + + followingCSv_entry = CS[:v_entry] + csId = csId - 1 + end #while + return movingSection +end #function secureBrakingBehavior! + +## define the intersection velocities between the characterisitc sections to secure accelerating behavior +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] + + CSs[1][:v_entry] = 0.0 # the entry velocity of the first characteristic section is 0.0 m/s + startingPoint = createDataPoint() + startingPoint[:i] = 1 + + previousCSv_exit = CSs[1][:v_entry] + for CS in CSs + 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) + acceleratingCourse::Vector{Dict} = [startingPoint] # List of data points + + if CS[:v_entry] < CS[:v_peak] + # conditions for entering the accelerating phase + stateFlags = Dict(:endOfCSReached => false, + :brakingStartReached => false, + :tractionDeficit => false, + :resistingForceNegative => false, + :previousSpeedLimitReached => false, + :speedLimitReached => false, + :error => false, + :usedForDefiningCharacteristics => true) # because usedForDefiningCharacteristics == true the braking distance will be ignored during securing the accelerating phase + v_peak = CS[:v_entry] + (CS, acceleratingCourse, stateFlags) = addBreakFreeSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) + while !stateFlags[:speedLimitReached] && !stateFlags[:endOfCSReached] + if !stateFlags[:tractionDeficit] + if !stateFlags[:previousSpeedLimitReached] + (CS, acceleratingCourse, stateFlags) = addAcceleratingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function changes the acceleratingCourse + + elseif stateFlags[:previousSpeedLimitReached] + (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] + 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 + end + end + v_peak = max(v_peak, acceleratingCourse[end][:v]) + end + +# CS[:v_peak] = max(CS[:v_entry], acceleratingCourse[end][:v]) + CS[:v_peak] = v_peak + CS[:v_exit] = min(CS[:v_exit], CS[:v_peak], acceleratingCourse[end][:v]) + else #CS[:v_entry] == CS[:v_peak] + # v_exit stays the same + end #if + + previousCSv_exit = CS[:v_exit] + + # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit + CS[:behaviorSections] = Dict() + CS[:E] = 0.0 + CS[:t] = 0.0 + end #for + + return movingSection +end #function secureAcceleratingBehavior! + + +#= +## define the intersection velocities between the characterisitc sections to secure cruising behavior +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] + + startingPoint = createDataPoint() + startingPoint[:i] = 1 + + previousCSv_exit = CSs[1][:v_entry] + + for CS in CSs + # conditions for entering the cruising phase + stateFlags = Dict(:endOfCSReached => false, + :brakingStartReached => false, + :tractionDeficit => false, + :resistingForceNegative => false, + :previousSpeedLimitReached => false, + :speedLimitReached => false, + :error => false, + :usedForDefiningCharacteristics => true) + + CS[:v_entry] = min(CS[:v_entry], previousCSv_exit) + + startingPoint[:s] = CS[:s_entry] + startingPoint[:v] = CS[:v_peak] + cruisingCourse::Vector{Dict} = [startingPoint] # List of data points + + while !stateFlags[:endOfCSReached] #&& s_cruising > 0.0 + if !stateFlags[:tractionDeficit] + s_cruising = CS[:s_exit] - cruisingCourse[end][:s] + if !stateFlags[:resistingForceNegative]# cruisingCourse[end][:F_R] >= 0 + (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") # this function changes the cruisingCourse + else + (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] + 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 + end + end + end + + CS[:v_exit] = min(CS[:v_exit], cruisingCourse[end][:v]) + + previousCSv_exit = CS[:v_exit] + + # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit + CS[:behaviorSections] = Dict() + CS[:E] = 0.0 + CS[:t] = 0.0 + end #for + + return movingSection +end #function secureCruisingBehavior! +=# diff --git a/src/export.jl b/src/export.jl new file mode 100644 index 0000000..7113866 --- /dev/null +++ b/src/export.jl @@ -0,0 +1,212 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +function exportToCsv(runningTime::AbstractFloat, settings::Settings) + createCsvFile(runningTime, settings) + + return true +end + +function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Settings) + createCsvFile(dataPointsToExport, settings) + + return true +end + +function exportToCsv(output::Dict) + 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][:outputDetail] == "points of interest" + dataPointsToExport = output[:pointsOfInterestMinimumRunningTime] + else + dataPointsToExport = output[:drivingCourseMinimumRunningTime] + end + createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) + end + if output[:settings][:operationModeMinimumEnergyConsumption] == true + operationMode = "minimum energy consumption" + if output[:settings][:outputDetail] == "points of interest" + dataPointsToExport = output[:pointsOfInterestMinimumEnergyConsumption] + else + dataPointsToExport = output[:drivingCourseMinimumEnergyConsumption] + end + createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) + end + return true + end + return false +end #function exportToCsv + +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 outputDir + date = Dates.now() + dateString = Dates.format(date, "yyyy-mm-dd_HH.MM.SS") + + csvFilePath = settings[:outputDir]*"/"*dateString*"_RunningTime.csv" + + CSV.write(csvFilePath, df, header=false) + println("The output CSV file has been created at ",csvFilePath) + + return true +end #function createCsvFile + + +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] + + allColumns = Array{Any,1}[] + for column in 1:length(header) + currentColumn = Any[] + push!(currentColumn, header[column]) + for point in dataPointsToExport + push!(currentColumn, point[columnSymbols[column]]) + end + push!(allColumns, currentColumn) + end # for + + + # 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 + println("") + end + + date = Dates.now() + dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") + csvFilePath=settings[:outputDir]*"/"*dateString*"_DataPoints.csv" + CSV.write(csvFilePath, df, header=false) + println("The output CSV file has been created at ",csvFilePath) + + return true +end #function createCsvFile + + +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) + + # create accumulated data block + accumulatedData = Array{Any, 1}[] + 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]] + push!(accumulatedData, row) # push row to accumulatedData + end + + #create information block + allColumns=Array{Any,1}[] + push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) + push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) + for column in 3:length(accumulatedData[1]) + push!(allColumns, ["", "", "", "", "", "", ""]) + end # for + + # add driving data to the array + header = accumulatedData[1] + for column in 1:length(accumulatedData[1]) + push!(allColumns[column], header[column]) + for row in accumulatedData[2:end] + push!(allColumns[column], row[column]) + end + end # for + + # 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" + elseif operationMode == "minimum energy consumption" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" + else + # should not be possible + end + CSV.write(csvFilePath, df, header=false) + println("The output CSV file has been created for ",operationMode," at ",csvFilePath) + + return true +end #function createCsvFile + + + +#= +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) + + # create accumulated data block + accumulatedData = Array{Any, 1}[] + 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 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]] + push!(accumulatedData, row) # push row to accumulatedData + end + end + + #create information block + allColumns=Array{Any,1}[] + push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) + push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) + for column in 3:length(accumulatedData[1]) + push!(allColumns, ["", "", "", "", "", "", ""]) + end # for + + # add driving data to the array + header = accumulatedData[1] + for column in 1:length(accumulatedData[1]) + push!(allColumns[column], header[column]) + for row in accumulatedData[2:end] + push!(allColumns[column], row[column]) + end + end # for + + # 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 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" + elseif operationMode == "minimum energy consumption" + csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" + else + # should not be possible + end + CSV.write(csvFilePath, df, header=false) + println("The output CSV file has been created for ",operationMode," at ",csvFilePath) + + return true +end #function createCsvFile +=# diff --git a/src/formulary.jl b/src/formulary.jl new file mode 100644 index 0000000..1be57a6 --- /dev/null +++ b/src/formulary.jl @@ -0,0 +1,226 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2022" +# __license__ = "ISC" + +######################### +## literature the driving dynamics equations are based on: +## +## @incollection{Bruenger:2014, % Chapter 4 +## author = {Brünger, Olaf and Dahlhaus, Elias}, +## year = {2014}, +## title = {Running Time Estimation}, +## pages = {65--90}, +## booktitle = {Railway Timetabling \& Operations.}, +## editora = {Hansen, Ingo A.}, +## editorb = {Pachl, Jörn}, +## isbn = {978-3-777-10462-1}, +## publisher = {Eurailpress DVV Media Group}, +## } +## @Book{Wende:2003, +## author = {Wende, Dietrich}, +## date = {2003}, +## title = {Fahrdynamik des Schienenverkehrs}, +## isbn = {978-3-322-82961-0}, +## publisher = {Springer-Verlag}, +## } +######################### + +approxLevel = 6 +v00 = 100/3.6 # velocity factor (in m/s) +g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2 + +## calculate forces + +#TODO: replace the ? ? ? +""" + calcTractionUnitResistance(v, train) + +Calculate the vehicle resistance for the traction unit of the `train` dependend on the velocity `v`. + +... +# Arguments +- `v::AbstractFloat`: the current velocity in m/s. +- `train::Dict`: ? ? ? +... + +# Examples +```julia-repl +julia> calcTractionUnitResistance(30.0, ? ? ?) +? ? ? +``` +""" +function calcTractionUnitResistance(v::AbstractFloat, train::Dict) + # equation is based on [Wende:2003, page 151] + f_Rtd0 = train[:f_Rtd0] # coefficient for basic resistance due to the traction units driving axles (in ‰) + f_Rtc0 = train[:f_Rtc0] # coefficient for basic resistance due to the traction units carring axles (in ‰) + F_Rt2 = train[:F_Rt2] # coefficient for air resistance of the traction units (in N) + m_td = train[:m_td] # mass on the traction unit's driving axles (in kg) + m_tc = train[:m_tc] # mass on the traction unit's carrying axles (in kg) + Δv_t = train[:Δv_t] # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) + + F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ + # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) + return F_R_tractionUnit + #TODO: same variable name like in the rest of the tool? return R_traction + #TODO: just one line? return train[:f_Rtd0]/1000*train[:m_td]*g+train[:f_Rtc0]/1000*train[:m_tc]*g+train[:F_Rt2]*((v+train[:Δv_t])/v00)^2 # /1000 because of the unit ‰ +end #function calcTractionUnitResistance + +""" +TODO +calculate and return the wagons vehicle resistance dependend on the velocity +""" +function calcWagonsResistance(v::AbstractFloat, train::Dict) + # equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135) + f_Rw0 = train[:f_Rw0] # coefficient for basic resistance of the set of wagons (consist) (in ‰) + f_Rw1 = train[:f_Rw1] # coefficient for the consists resistance to rolling (in ‰) + f_Rw2 = train[:f_Rw2] # coefficient fo the consistsr air resistance (in ‰) + m_w = train[:m_w] # mass of the set of wagons (consist) (in kg) + Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) + + F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_w) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ +# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_w) /v00)^2 # vehicle resistance of the wagons (in N) + return F_R_wagons +end #function calcWagonsResistance + +function calcForceFromCoefficient(f_R::Real, m::Real) + # equation is based on [Wende:2003, page 8] + + # f_R: specific resistance (in ‰) + # m: vehicle's mass (in kg) + + F_R = f_R /1000 *m *g # Resisting Force (in N) # /1000 because of the unit ‰ + return F_R +end #function calcForceFromCoefficient + +function calcAcceleration(F_T::Real, F_R::Real, m_train::Real, ξ_train::Real) + # equation is based on [Bruenger:2014, page 72] with a=dv/dt + + # F_T: tractive effort (in N) + # F_R: resisting forces (in N) + # m_train: train's mass (in kg) + # ξ_train: train's rotation mass factor (without unit) + + a = (F_T - F_R) /m_train /ξ_train # acceleration (in m/s) + return a +end #function calcAcceleration + +function calc_Δs_with_Δt(Δt::Real, a_prev::Real, v_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δt: time step (in s) + # a_prev: acceleration from previous data point + # v_prev: velocitiy from previous data point + Δs = Δt * (2*v_prev + Δt*a_prev) /2 # step size (in m) + return Δs +end #function calc_Δs_with_Δt + +function calc_Δs_with_Δv(Δv::Real, a_prev::Real, v_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δv: velocity step (in m/s) + # a_prev: acceleration from previous data point + # v_prev: velocitiy from previous data point + Δs = ((v_prev + Δv)^2 - v_prev^2)/2/a_prev # step size (in m) + return Δs +end #function calc_Δs_with_Δv + +function calc_Δt_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δs: distance step (in m) + # a_prev: acceleration from previous data point + # v_prev: velocitiy from previous data point + + Δt = sign(a_prev) *sqrt((v_prev /a_prev)^2 + 2 *Δs /a_prev) - v_prev /a_prev # step size (in m/s) + return Δt +end #function calc_Δt_with_Δs + +function calc_Δt_with_Δv(Δv::Real, a_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δv: velocity step (in m/s) + # a_prev: acceleration from previous data point + Δt = Δv /a_prev # step size (in s) + return Δt +end #function calc_Δt_with_Δv + +function calc_Δt_with_constant_v(Δs::Real, v::Real) + # equation is based on [Wende:2003, page 37] + + # Δs: distance step (in m) + # v: constant velocity (in m/s) + Δt = Δs /v # step size (in s) + return Δt +end #function calc_Δt_with_constant_v + +function calc_Δv_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δs: distance step (in m) + # a_prev: acceleration from previous data point + # v_prev: velocitiy from previous data point + Δv = sqrt(v_prev^2 + 2*Δs*a_prev) - v_prev # step size (in m/s) + return Δv +end #function calc_Δv_with_Δs + +function calc_Δv_with_Δt(Δt::Real, a_prev::Real) + # equation is based on [Wende:2003, page 37] + + # Δt: time step (in s) + # a_prev: acceleration from previous data point + Δv = Δt * a_prev # step size (in m/s) + return Δv +end #function calc_Δv_with_Δt + +function calc_ΔW(F_T_prev::Real, Δs::Real) + # equation is based on [Wende:2003, page 17] + + # F_T_prev: tractive force from previous data point + # Δs: distance step + ΔW = F_T_prev * Δs # mechanical work in this step (in Ws) + return ΔW +end #function calc_ΔW + +function calc_ΔE(ΔW::Real) + # simplified equation + # TODO! + # ΔW: mechanical work in this step (in Ws) + ΔE = ΔW # energy consumption in this step (in Ws) + return ΔE +end #function calc_ΔW + +function calcBrakingDistance(v_start::Real, v_end::Real, a_braking::Real) + # equation is based on [Wende:2003, page 37] + + # v_start: velocity at the start of braking (in m/s) + # v_end: target velocity at the end of braking (in m/s) + # 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=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) + # equation is based on [Wende:2003, page 37] + + # v_end: target velocity at the end of braking (in m/s) + # 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=approxLevel) + return floor(v_start, digits=approxLevel +1) +end #function calcBrakingStartVelocity + +function calcBrakingAcceleration(v_start::Real, v_end::Real, s_braking::Real) + # equation is based on [Wende:2003, page 37] + + # v_start: braking start velocity (in m/s) + # v_end: target velocity at the end of braking (in m/s) + # s_braking: braking distance (in Ws) + a_braking = (v_end^2 - v_start^2) /2 /s_braking # constant braking acceleration (in m/s^2) + return a_braking +end #function calcBrakingAcceleration diff --git a/src/import.jl b/src/import.jl new file mode 100644 index 0000000..aae95af --- /dev/null +++ b/src/import.jl @@ -0,0 +1,35 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +""" +Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. +""" +function importYamlFiles(trainDirectory::String, pathDirectory::String) + train = importFromYaml(:train, trainDirectory) + path = importFromYaml(:path, pathDirectory) + + return (train, path) +end #function importYamlFiles + + """ + Read the train information from a YAML file, save it in a Dict and return it. + """ +function importFromYaml(dataType::Symbol, directory::String) + dataSet = String(dataType) + data = YAML.load(open(directory)) + if collect(keys(data))[1] != dataSet + error("ERROR at reading the ", dataSet, " yaml file: The data set is called ", collect(keys(data))[1]," and not ", dataSet, ".") + end + dataKeys = collect(keys(data[dataSet])) + dataKeys = collect(keys(data[dataSet])) + dataValues = collect(values(data[dataSet])) + dictionary = Dict() + for number in 1:length(dataKeys) + merge!(dictionary, Dict(Symbol(dataKeys[number]) => dataValues[number])) + end + return dictionary +end # function importFromYaml diff --git a/src/output.jl b/src/output.jl new file mode 100644 index 0000000..a21c12e --- /dev/null +++ b/src/output.jl @@ -0,0 +1,106 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg" +# __copyright__ = "2020-2022" +# __license__ = "ISC" + +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.outputDetail == :points_of_interest + # add points of interest + if haskey(path, :pointsOfInterest) + output = Vector{Dict}() + POI = 1 + i = 1 + while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] + if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + push!(output, drivingCourse[i]) + POI = POI+1 + end + i = i+1 + end + end + + elseif settings.outputDetail == :driving_course + output = drivingCourse + + elseif settings.outputDetail == :everything + output = Dict{Symbol,Any}() + merge!(output, Dict(:train => train, :path => path, :settings => settings)) + + + # add moving section and driving courses + if settings[:operationModeMinimumRunningTime] == true + merge!(output, Dict(:movingSectionMinimumRunningTime => movingSection, + :drivingCourseMinimumRunningTime => drivingCourse)) + elseif settings[:operationModeMinimumEnergyConsumption] == true + merge!(output, Dict(:movingSectionMinimumEnergyConsumption => movingSection, + :drivingCourseMinimumEnergyConsumption => drivingCourse)) + end + + # add points of interest + if haskey(path, :pointsOfInterest) + pointsOfInterest = Vector{Dict}() + POI = 1 + i = 1 + while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] + if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + push!(pointsOfInterest, drivingCourse[i]) + POI = POI+1 + end + i = i+1 + end + + if settings[:operationModeMinimumRunningTime] == true + merge!(output, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) + elseif settings[:operationModeMinimumEnergyConsumption] == true + merge!(output, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) + end + end + else + output = nothing + end + return output +end + +#= +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)) + + + # add moving section and driving courses + if settings[:operationModeMinimumRunningTime] == true + merge!(outputDict, Dict(:movingSectionMinimumRunningTime => movingSection, + :drivingCourseMinimumRunningTime => drivingCourse)) + elseif settings[:operationModeMinimumEnergyConsumption] == true + merge!(outputDict, Dict(:movingSectionMinimumEnergyConsumption => movingSection, + :drivingCourseMinimumEnergyConsumption => drivingCourse)) + end + + # add points of interest + if haskey(path, :pointsOfInterest) + pointsOfInterest = Vector{Dict}() + POI = 1 + i = 1 + while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] + if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + push!(pointsOfInterest, drivingCourse[i]) + POI = POI+1 + end + i = i+1 + end + + if settings[:operationModeMinimumRunningTime] == true + merge!(outputDict, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) + elseif settings[:operationModeMinimumEnergyConsumption] == true + merge!(outputDict, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) + end + end + + return outputDict +end # function createOutputDict +=# diff --git a/src/types.jl b/src/types.jl new file mode 100644 index 0000000..3419b44 --- /dev/null +++ b/src/types.jl @@ -0,0 +1,799 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Max Kannenberg, Martin Scheidt" +# __copyright__ = "2022" +# __license__ = "ISC" + +""" + Settings(file) + +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 + + 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::Settings) + checkAndSetTrain!(train) + checkAndSetPath!(path) + + 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! + +""" +Read the train information from a YAML file, save it in a train Dict and return it. +""" +function checkAndSetTrain!(train::Dict) + # check train information from input dictionary + + checkAndSetString!(train, "train", :name, "") # train's name + # add train's identifier if not existing + if !(haskey(train, :id) && train[:id]!=nothing) + merge!(train, Dict(:id =>1)) + end + checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight" + + checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) + # TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) + + checkAndSetSpeedLimit!(train) # train's speed limit (in m/s) + checkAndSetBrakingAcceleration!(train) # a_braking + + checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg) + checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg) + checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg) + + checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg) + checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg) + if train[:m_train] <= 0.0 + error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.") + end + + checkAndSetRotationMassFactors!(train) + checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort + + # coefficients for the vehicle resistance of the traction unit + checkAndSetRealNumber!(train, "train", :Δv_t, "m/s", 15.0/3.6) # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) + checkAndSetPositiveNumber!(train, "train", :f_Rtd0, "‰", 0.0) # coefficient for basic resistance due to the traction units driving axles (in ‰) + checkAndSetPositiveNumber!(train, "train", :f_Rtc0, "‰", 0.0) # coefficient for basic resistance due to the traction units carring axles (in ‰) + checkAndSetPositiveNumber!(train, "train", :F_Rt2, "N", 0.0) # coefficient for air resistance of the traction units (in N) + + # coefficients for the vehicle resistance of the set of wagons (consist) + checkAndSetRealNumber!(train, "train", :Δv_w, "m/s", getDefault_Δv_w(train[:type])) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) + checkAndSetPositiveNumber!(train, "train", :f_Rw0, "‰", 0.0) # coefficient for basic resistance of the set of wagons (consist) (in ‰) + checkAndSetPositiveNumber!(train, "train", :f_Rw1, "‰", 0.0) # coefficient for the consists resistance to rolling (in ‰) + checkAndSetPositiveNumber!(train, "train", :f_Rw2, "‰", 0.0) # coefficient fo the consistsr air resistance (in ‰) + + # inform the user about keys of the input dictionary that are not used in this tool + usedKeys = [:name, :id, :type, + :length, :l_train, :v_limit, :v_limit_kmh, :a_braking, + :m_train, :m_t, :m_td, :m_tc, :m_w, + :ξ_train, :ξ_t, :ξ_w, :rotationMassFactor_train, :rotationMassFactor_t, :rotationMassFactor_w, + :tractiveEffortVelocityPairs, :F_T_pairs, :F_T_pairs_kmh, + :f_Rtd0, :f_Rtc0, :F_Rt2, :Δv_t, + :f_Rw0, :f_Rw1, :f_Rw2, :Δv_w] + informAboutUnusedKeys(collect(keys(train)), usedKeys::Vector{Symbol}, "train") + + return train +end #function checkAndSetTrain! + +function checkAndSetPath!(path::Dict) + # check path information from input dictionary + + checkAndSetString!(path, "path", :name, "") + # TODO checkId ? path[:id] # path identifier + checkAndSetSections!(path) + checkAndSetPOIs!(path) + + # inform the user about keys of the input dictionary that are not used in this tool + usedKeys = [:name, + :sections, :sectionStarts, :sectionStarts_kmh, + :pointsOfInterest] + informAboutUnusedKeys(collect(keys(path)), usedKeys::Vector{Symbol}, "path") + + return path +end # function checkAndSetPath! + +function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::Bool) + if haskey(dictionary,key) && dictionary[key]!=nothing + if typeof(dictionary[key]) != Bool + error("ERROR at checking the input for the ",dictionaryType,": The value of the key ",String(key)," is not correct. The value has to be of type Bool.") + end + else + merge!(dictionary, Dict(key => defaultValue)) + defaultValue && println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.") + end + return dictionary +end #function checkAndSetBool! + +function checkAndSetPositiveNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) + if haskey(dictionary,key) && dictionary[key]!=nothing + if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0 + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") + end + else + merge!(dictionary, Dict(key => default)) + println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) + end + + return dictionary +end #function checkAndSetPositiveNumber! + +# first method without a default value +function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String) + mainKey_temp = -1.0 + alternativeKey_temp = -1.0 + + if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing + if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 + mainKey_temp = dictionary[mainKey] + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") + end + end + + if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing + if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 + alternativeKey_temp = dictionary[alternativeKey] + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") + end + else + delete!(dictionary, alternativeKey) + end + + if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 + difference = abs(mainKey_temp - alternativeKey_temp) + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? + delete!(dictionary, alternativeKey) + println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) + end + elseif mainKey_temp >= 0.0 + # do nothing + elseif alternativeKey_temp >= 0.0 + merge!(dictionary, Dict(mainKey => alternativeKey_temp)) + else + # do nothing + end + + return dictionary +end #function checkAndSetPositiveNumberWithDifferentNames! + +# second method with a default value +function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String, default::Real) + mainKey_temp = -1.0 + alternativeKey_temp = -1.0 + + if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing + if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 + mainKey_temp = dictionary[mainKey] + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") + end + end + + if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing + if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 + alternativeKey_temp = dictionary[alternativeKey] + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") + end + else + delete!(dictionary, alternativeKey) + end + + if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 + difference = abs(mainKey_temp - alternativeKey_temp) + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? + delete!(dictionary, alternativeKey) + println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) + end + elseif mainKey_temp >= 0.0 + # do nothing + elseif alternativeKey_temp >= 0.0 + merge!(dictionary, Dict(mainKey => alternativeKey_temp)) + else + # set a default value + merge!(dictionary, Dict(mainKey, default)) + println("INFO at checking the input for the ",dictionaryType,": The key ",mainKey," or its value is missing. Therefore the value ",String(mainKey),"=",default," ",unit," is used." ) + end + + return dictionary +end #function checkAndSetPositiveNumberWithDifferentNames! + +function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) + if haskey(dictionary,key) && dictionary[key]!=nothing + if typeof(dictionary[key]) <: Real + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.") + end + else + merge!(dictionary, Dict(key => default)) + println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) + end + + return dictionary +end #function checkAndSetRealNumber! + +function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::Symbol, summand1::Symbol, summand2::Symbol) + if haskey(dictionary,sum) && dictionary[sum]!=nothing + if typeof(dictionary[sum]) <: Real && dictionary[sum] >= 0.0 + difference = abs(dictionary[sum] - (dictionary[summand1]+dictionary[summand2])) + if difference > 1/(10^approxLevel) + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") + end + else + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.") + end + else + merge!(dictionary, Dict(sum => dictionary[summand1]+dictionary[summand2])) + println("INFO at checking the input for the ",dictionaryType,": The key ",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." ) + end + + return dictionary +end #function checkAndSetSum! + +function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String, validValues::Vector{String}) + # TODO change checkAndAddString! to checkAndAddSymbol! ? + if haskey(dictionary,key) && dictionary[key]!=nothing + value = dictionary[key] + if typeof(value) == String + for validValue in validValues + if value == validValue + return dictionary + end + end + end + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues) + else + println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. It has to be one of the following String values: ", validValues,". For this calculation the default value '",defaultValue,"' will be used.") + merge!(dictionary, Dict(key => defaultValue)) + end + return dictionary +end #function checkAndSetString! +# second method of function checkAndSetString! without validValues +function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String) + if haskey(dictionary,key) && dictionary[key]!=nothing + value = dictionary[key] + if typeof(value) == String + return dictionary + end + error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.") + else + println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. For this calculation the default value '",defaultValue,"' will be used.") + merge!(dictionary, Dict(key => defaultValue)) + end + return dictionary +end #function checkAndSetString! + +function checkAndSetSpeedLimit!(train::Dict) + v_limit_temp = 0.0 + v_limit_kmh_temp = 0.0 + + if haskey(train, :v_limit) && train[:v_limit]!=nothing + if typeof(train[:v_limit]) <: Real && train[:v_limit] >= 0.0 + v_limit_temp = train[:v_limit] + else + error("ERROR at checking the input for the train: The value of v_limit is no real floating point number >=0.0.") + end + end + + if haskey(train, :v_limit_kmh) && train[:v_limit_kmh]!=nothing + if typeof(train[:v_limit_kmh]) <: Real && train[:v_limit_kmh] >= 0.0 + v_limit_kmh_temp = train[:v_limit_kmh] + else + error("ERROR at checking the input for the train: The value of v_limit_kmh is no real floating point number >=0.0.") + end + else + delete!(train, :v_limit_kmh) + end + + if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0 + difference = abs(v_limit_temp - v_limit_kmh_temp/3.6) + if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? + delete!(train, :v_limit_kmh) + println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." ) + end + elseif v_limit_temp > 0.0 + # do nothing + elseif v_limit_kmh_temp > 0.0 + merge!(train, Dict(:v_limit => v_limit_kmh_temp/3.6)) + else + # set a default value + merge!(train, Dict(:v_limit, 1000.0/3.6)) # set speed limit to 1000 km/h + println("INFO at checking the input for the train: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",train[:v_limit]," m/s is used." ) + end + + return train +end #function checkAndSetSpeedLimit! + +function checkAndSetBrakingAcceleration!(train::Dict) + if haskey(train, :a_braking) && train[:a_braking]!=nothing + if typeof(train[:a_braking]) <: Real + if train[:a_braking] > 0.0 + train[:a_braking] =-train[:a_braking] + println("INFO at checking the input for the train: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking=",train[:a_braking]," m/s^2 is used." ) + elseif train[:a_braking] == 0.0 + error("ERROR at checking the input for the train: The value for a_braking is 0.0. The braking acceleration has to be <0.0.") + end + else + error("ERROR at checking the input for the train: The value for a_braking is no real floating point number <0.0.") + end + else + # set a default value depending on the train type + if train[:type] == "freight" + a_braking = -0.225 + elseif train[:type] == "passenger" + a_braking = -0.375 + #elseif train[:type] == "passengerSuburban" + # a_braking = -0.525 + # TODO: add suburban trains to train type? + end + + merge!(train, Dict(:a_braking => a_braking)) + println("INFO at checking the input for the train: The key for a_braking is missing. Because of the train type ",train[:type]," a_braking=",a_braking," m/s^2 will be assumed and used." ) + end + + return train +end #function checkAndSetBrakingAcceleration! + +function checkAndSetRotationMassFactors!(train::Dict) + checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_train, :rotationMassFactor_train, "") + checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_t, :rotationMassFactor_t, "") + checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_w, :rotationMassFactor_w, "") + if haskey(train, :ξ_train) && train[:ξ_train]!=nothing + if train[:ξ_train]>0.0 + if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing)) + # TODO: is && train[:ξ_t]>0.0 necessary here? + difference = abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train]) + if difference > 1/(10^approxLevel) + error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") + end + end + else + error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.") + end + else + checkAndSetPositiveNumber!(train, "train", :ξ_t, "", 1.09) + + if train[:m_w]>0.0 + default_ξ_w = 1.06 + else + default_ξ_w = 0.0 + end + checkAndSetPositiveNumber!(train, "train", :ξ_w, "", default_ξ_w) + + + ξ_train=(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit) + if ξ_train <= 0.0 + error("ERROR at checking the input for the train: The train's rotations mass factor has to be higher than 0.0 kg.") + end + merge!(train, Dict(:ξ_train => ξ_train)) + end + + return train +end #function checkAndSetRotationMassFactors! + +function checkAndSetTractiveEffortVelocityPairs!(train::Dict) # pairs of velocity and tractive effort + if haskey(train,:tractiveEffortVelocityPairs) && train[:tractiveEffortVelocityPairs]!=nothing + pairs = train[:tractiveEffortVelocityPairs] + velocityMultiplier = 1.0 + + if (haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing) && (haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing) + println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs, F_T_pairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) + + elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing + println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs. The values for tractiveEffortVelocityPairs are used." ) + + elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing + println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) + end + + elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing + pairs = train[:F_T_pairs] + velocityMultiplier = 1.0 + + if haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing + println("WARNING at checking the input for the train: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." ) + end + + elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing + velocityMultiplier = 1000/3600 + pairs=[] + for row in 1:length(train[:F_T_pairs_kmh]) + push!(pairs, [train[:F_T_pairs_kmh][row][1]*velocityMultiplier, train[:F_T_pairs_kmh][row][2]]) + end # for + + else + error("ERROR at checking the input for the train: There has to be the key tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.") + end # if + + # check if the elements of the array have the correct type + errorDetected=false + + for row in 1:length(pairs) + if typeof(pairs[row][1]) <: Real && pairs[row][1]>=0.0 + else + errorDetected=true + println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") + end + if typeof(pairs[row][2]) <: Real && pairs[row][2]>=0.0 + else + errorDetected=true + println("ERROR at checking the input for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") + end + + if row>=2 && pairs[row][1] <= pairs[row-1][1] + errorDetected=true + println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," (v=",pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",pairs[row-1][1]," m/s).") + end + end # for + if errorDetected + error("ERROR at checking the input for the train: Only real floating point number >=0.0 are allowed for speed and tractive effort. The speed values have to be listed from low to high.") + end + + # create tractiveEffortVelocityPairs + if pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used + newPairs=[] + push!(newPairs, [0.0, pairs[1][2]]) + println("INFO at checking the input for the train: The tractive effort for v=0.0 m/s is missing. Therefore the first given value F_T(v=",pairs[1][1]," m/s)=",pairs[1][2]," N will be used." ) + for row in 1:length(pairs) + push!(newPairs, [pairs[row][1], pairs[row][2]]) + end # for + merge!(train, Dict(:tractiveEffortVelocityPairs => newPairs)) + else + merge!(train, Dict(:tractiveEffortVelocityPairs => pairs)) + end + + if length(pairs[1])>2 + println("INFO according the train dictionary: Only the first two columns of train[:tractiveEffortVelocityPairs] are used in this tool.") + end + + return train +end #function checkAndSetTractiveEffortVelocityPairs! + +function getDefault_Δv_w(type::String) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) + if type == "passenger" + # TODO if different passenger or freight trains are posiible, use: if startswith(type, "passenger"). exanples: passengerLocomotivehauled and passengerMotorCoachTrain + Δv_w=15.0/3.6 + elseif type == "freight" + Δv_w=0.0 + end # if + + return Δv_w +end #function getDefault_Δv_w! + +function checkAndSetSections!(path::Dict) + # check the section information + if haskey(path,:sections) && path[:sections]!=nothing + # TODO: check typeof(path[:sections]) == Dict + if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) + println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) + + elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing + println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) + + elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing + println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) + end + elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing + # TODO: check typeof(path[:sections]) == Array + createSections!(path, :sectionStarts) + + if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing + println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) + end + elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing + # TODO: check typeof(path[:sections]) == Array + createSections!(path, :sectionStarts_kmh) + else + error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") + section = Dict(:s_start => 0.0, + :s_end => 15.0, + :v_limit => 1000.0/3.6, + :f_Rp => 0.0) + merge!(path, Dict(:sections => [section])) + return path + end + + sections = path[:sections] + + checkedSections = [] + increasing = false + decreasing = false + + #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? + + # check values for section==1 + checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m", 0.0) # first point of the section (in m) + checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m", 0.0) # first point of the next section (in m) + checkAndSetPositiveNumber!(sections[1], "path[:sections][1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) + checkAndSetRealNumber!(sections[1], "path[:sections][1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) + + push!(checkedSections, sections[1]) + + if sections[1][:s_start] < sections[1][:s_end] + increasing = true + elseif sections[1][:s_start] > sections[1][:s_end] + decreasing = true + else + pop!(checkedSections) + println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") + end + + + for sectionNr in 2:length(sections) + checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) + # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values + #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real + # defaultEnd = sections[sectionNr+1][:s_start] + #end + defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! + checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) + checkAndSetPositiveNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) + checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) + + push!(checkedSections, sections[sectionNr]) + + # compare the section's start and end position + if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] + increasing = true + elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] + decreasing = true + else + pop!(checkedSections) + println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") + end + if increasing && decreasing + error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") + end + + + if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] + error("ERROR at checking the input for the path[:sections]: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") + # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? + end + end #for + + return path +end #function checkAndSetSections! + +function createSections!(path::Dict, key::Symbol) + # read the section starting positions and corresponding information + if key == :sectionStarts + sectionStartsArray = path[:sectionStarts] + conversionFactor = 1.0 # conversion factor between the units m/s and m/s + + if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing + println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) + end + elseif key == :sectionStarts_kmh + sectionStartsArray = path[:sectionStarts_kmh] + conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s + else + error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") + end # if + + # check if the array is correct and if elements of the array have the correct type and valid values + errorDetected = false + if length(sectionStartsArray)<2 + error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") + end + + for row in 1:length(sectionStartsArray) + if length(sectionStartsArray[row])>=3 + if length(sectionStartsArray[row])>3 + println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") + end + else + error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") + end + + if !(typeof(sectionStartsArray[row][1]) <: Real) + errorDetected=true + println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") + end + if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) + errorDetected=true + println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") + end + if !(typeof(sectionStartsArray[row][3]) <: Real) + errorDetected=true + println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") + end + end # for + if errorDetected + error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") + end + + + sections = [] + for row in 2:length(sectionStartsArray) + s_start = sectionStartsArray[row-1][1] # first point of the section (in m) + s_end = sectionStartsArray[row][1] # first point of the next section (in m) + v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) + f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) + + section = Dict(:s_start => s_start, + :s_end => s_end, + :v_limit => v_limit, + :f_Rp => f_Rp) + push!(sections, section) + end # for + # s_start in first entry defines the path's beginning + # s_end in last entry defines the path's ending + + merge!(path, Dict(:sections => sections)) + return path +end #function createSections! + +function checkAndSetPOIs!(path::Dict) + # read the section starting positions and corresponding information + if haskey(path, :pointsOfInterest) + if path[:pointsOfInterest] != nothing + pointsOfInterest = path[:pointsOfInterest] + + sortingNeeded = false + errorDetected = false + for element in 1:length(pointsOfInterest) + if typeof(pointsOfInterest[element]) <: Real + if element > 1 + if pointsOfInterest[element] < pointsOfInterest[element-1] + sortingNeeded = true + println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") + end + end + else + errorDetected = true + println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") + end + end # for + + if errorDetected + error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") + end + if sortingNeeded == true + sort!(pointsOfInterest) + end + + copiedPOIs = [] + for element in 1:length(pointsOfInterest) + if element == 1 + push!(copiedPOIs, pointsOfInterest[element]) + elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] + push!(copiedPOIs, pointsOfInterest[element]) + end + end # for + path[:pointsOfInterest] = copiedPOIs + + else + println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") + delete!(path, :pointsOfInterest) + end + end + + return path +end #function checkAndSetPOIs! + +#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool +function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool + unusedKeys = [] + # find unused keys in allKeys + for key in allKeys + used = false + for usedKey in usedKeys + if key == usedKey + used = true + break + end + end + if !used + push!(unusedKeys, key) + end + end + + if length(unusedKeys)>0 + println("INFO at checking the input for the ",dictionaryType,": The following Keywords are not used in this tool:") + for key in unusedKeys + println(" - ",key) + end + end +end #function informAboutUnusedKeys From 80388e635f82cbb922eaa52c781b87daec9bdc19 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:33:45 +0200 Subject: [PATCH 04/18] correct lower/upper case pickup --- src/Behavior.jl | 1398 ---------------------------------------- src/Calc.jl | 160 ----- src/Characteristics.jl | 241 ------- src/Export.jl | 212 ------ src/Formulary.jl | 226 ------- src/Import.jl | 35 - src/Output.jl | 106 --- src/Types.jl | 799 ----------------------- 8 files changed, 3177 deletions(-) delete mode 100644 src/Behavior.jl delete mode 100644 src/Calc.jl delete mode 100644 src/Characteristics.jl delete mode 100644 src/Export.jl delete mode 100644 src/Formulary.jl delete mode 100644 src/Import.jl delete mode 100644 src/Output.jl delete mode 100644 src/Types.jl diff --git a/src/Behavior.jl b/src/Behavior.jl deleted file mode 100644 index d1815d2..0000000 --- a/src/Behavior.jl +++ /dev/null @@ -1,1398 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -## functions for calculating tractive effort and resisting forces -""" - calculateTractiveEffort(v, tractiveEffortVelocityPairs) - -Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` dependend on the velocity `v`. - -... -# Arguments -- `v::AbstractFloat`: the current velocity in m/s. -- `tractiveEffortVelocityPairs::Array{Array{AbstractFloat,1},1}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. -... - -# Examples -```julia-repl -julia> calculateTractiveEffort(20.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) -100000 - -julia> calculateTractiveEffort(30.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) -80000 -``` -""" -function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs) - for row in 1:length(tractiveEffortVelocityPairs) - nextPair = tractiveEffortVelocityPairs[row] - if nextPair[1] == v - return nextPair[2] - elseif nextPair[1] > v - # interpolate for a straight line between the two surrounding points with the formula: F=(v-v_(row-1))*(F_row-F_(row-1))/(v_row-v_(row-1))+F_(row-1) - previousPair = tractiveEffortVelocityPairs[row-1] - F_T_interpolation = (v-previousPair[1]) * (nextPair[2]-previousPair[2]) / (nextPair[1]-previousPair[1]) + previousPair[2] - return F_T_interpolation - end #if - end #for - # if v gets higher than the velocities in tractiveEffortVelocityPairs the last tractive effort will be used - # TODO: also an extrapolation could be used - return tractiveEffortVelocityPairs[end][2] -end #function calculateTractiveEffort - -""" -calculate and return the path resistance dependend on the trains position and mass model -""" -function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) - - if massModel == :mass_point - pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) - 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] - pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train[:length] * calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) - csId = csId-1 - if csId == 0 - # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used - return pathResistance + (CSs[1][:s_entry] - s_rear) / train[:length] * calcForceFromCoefficient(CSs[1][:r_path], train[:m_train]) - end #if - end #while - end #if - - return pathResistance -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) - # calculate resisting forces - dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) - dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) - dataPoint[:R_train] = dataPoint[:R_traction] + dataPoint[:R_wagons] - dataPoint[:R_path] = calculatePathResistance(CSs, csId, dataPoint[:s], massModel, train) - dataPoint[:F_R] = dataPoint[:R_train] + dataPoint[:R_path] - - # calculate tractive effort - if bsType == "braking" || bsType == "coasting" - dataPoint[:F_T] = 0.0 - elseif bsType == "cruising" - dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])) - else # bsType == "accelerating" || bsType == "diminishing" || 'default' - dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs]) - end - - return dataPoint -end #function calculateForces! - - -""" -TODO -""" -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. =# - - # create the next data point - newPoint = createDataPoint() - newPoint[:i] = previousPoint[:i]+1 # identifier - - # calculate s, t, v, E - if stepVariable == :distance # distance step method - newPoint[:Δs] = stepSize # step size (in m) - 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,".") - end - newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = 0.0 # step size (in m/s) - else - # check if the parts of the following square roots will be <0.0 in the functions calc_Δt_with_Δs and calc_Δv_with_Δs - squareRootPartIsNegative = (previousPoint[:v]/previousPoint[:a])^2+2*newPoint[:Δs]/previousPoint[:a] < 0.0 || previousPoint[:v]^2+2*newPoint[:Δs]*previousPoint[:a] < 0.0 - if previousPoint[:a] < 0.0 && squareRootPartIsNegative - error("ERROR: The train stops during the accelerating section in CS",csId," because the tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",previousPoint[:s]," m, v=",previousPoint[:v]," m/s, a=",previousPoint[:a]," m/s^2,", - " F_T=",previousPoint[:F_T]," N, R_traction=",previousPoint[:R_traction]," N, R_wagons=",previousPoint[:R_wagons]," N, R_path=",previousPoint[:R_path]," N.") - end - newPoint[:Δt] = calc_Δt_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = calc_Δv_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in m/s) - end - - 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 == :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,".") - end - newPoint[:Δs] = stepSize # step size (in m) - # TODO what is the best default step size for constant v? define Δs or Δt? - newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = 0.0 # step size (in m/s) - else - newPoint[:Δv] = stepSize * sign(previousPoint[:a]) # step size (in m/s) - newPoint[:Δs] = calc_Δs_with_Δv(newPoint[:Δv], previousPoint[:a], previousPoint[:v]) # step size (in m) - newPoint[:Δt] = calc_Δt_with_Δv(newPoint[:Δv], previousPoint[:a]) # step size (in s) - end - end #if - - newPoint[:s] = previousPoint[:s] + newPoint[:Δs] # position (in m) - newPoint[:t] = previousPoint[:t] + newPoint[:Δt] # point in time (in s) - newPoint[:v] = previousPoint[:v] + newPoint[:Δv] # velocity (in m/s) - newPoint[:ΔW] = calc_ΔW(previousPoint[:F_T], newPoint[:Δs]) # mechanical work in this step (in Ws) - newPoint[:W] = previousPoint[:W] + newPoint[:ΔW] # mechanical work (in Ws) - newPoint[:ΔE] = calc_ΔE(newPoint[:ΔW]) # energy consumption in this step (in Ws) - newPoint[:E] = previousPoint[:E] + newPoint[:ΔE] # energy consumption (in Ws) - - - return newPoint -end #function moveAStep - -""" -# 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 -""" -function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::Real, trainLength::Real) - v_limit = CSs[csWithTrainHeadId][:v_limit] - s_exit = CSs[csWithTrainHeadId][:s_exit] - if csWithTrainHeadId > 1 && s -trainLength < CSs[csWithTrainHeadId][:s_entry] - formerCsId = csWithTrainHeadId-1 - while formerCsId > 0 && s -trainLength < CSs[formerCsId][:s_exit] - if CSs[formerCsId][:v_limit] < v_limit # TODO: is the position of the train's rear < movingSection[:s_entry], v_limit of the first CS is used - v_limit = CSs[formerCsId][:v_limit] - s_exit = CSs[formerCsId][:s_exit] - end - formerCsId = formerCsId -1 - end - end - currentSpeedLimit = Dict(:v => v_limit, :s_end => s_exit + trainLength) - return currentSpeedLimit -end #function getCurrentSpeedLimit - -function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real) - for s_POI in pointsOfInterest - if s_POI > s - return s_POI - end - end - error("ERROR in getNextPointOfInterest: There is no POI higher than s=",s," m.") -end #function getNextPointOfInterest - -## This function calculates the data points of the breakFree section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. -# Info: currently the values of the breakFree section will be calculated like in the accelerating section -function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # conditions for the break free section - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - trainIsHalting = drivingCourse[end][:v] == 0.0 - - if trainIsHalting && !endOfCSReached - BS = createBehaviorSection("breakFree", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - 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 - - # 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) - catch(acceleratingError) - println("This error happened during the break free phase that is using the accelerating function:") - rethrow(acceleratingError) - end - - # delete every dataPoint except the first two - while drivingCourse[end][:i] > drivingCourse[BS[:dataPoints][1]][:i] +1 - pop!(drivingCourse) - end - - # change the accelerating data to break free - drivingCourse[end-1][:behavior] = BS[:type] - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - # remove the accelerating section from the CS - CS[:t] = CS[:t] - get(CS[:behaviorSections], :accelerating, Dict(:t=>0.0))[:t] # total running time (in s) - CS[:E] = CS[:E] - get(CS[:behaviorSections], :accelerating, Dict(:E=>0.0))[:E] # total energy consumption (in Ws) - delete!(CS[:behaviorSections], :accelerating) - - # calculate the accumulated breakFree section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:breakFree => BS)) - end # else: return the characteristic section without a breakFree section - - # determine state flags - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - s_braking = 0.0 - else - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # reset state flags - stateFlags[:endOfCSReached] = drivingCourse[end][:s] >= CS[:s_exit] - stateFlags[:brakingStartReached] = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] # or add another flag for equal forces? - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:previousSpeedLimitReached] = false - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_limit] - stateFlags[:error] = !(stateFlags[:endOfCSReached] || stateFlags[:brakingStartReached] || stateFlags[:tractionDeficit] || stateFlags[:previousSpeedLimitReached] || stateFlags[:speedLimitReached]) - - return (CS, drivingCourse, stateFlags) -end #function addBreakFreeSection! - -## This function calculates the data points of the clearing section. - # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. -function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - if stateFlags[:previousSpeedLimitReached] - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - 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) - # stateFlags[:brakingStartReached] = brakingStartReached - # stateFlags[:endOfCSReached] = stateFlags[:endOfCSReached] || drivingCourse[end][:s] == CS[:s_exit] - else - error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id]) - end - #stateFlags[:previousSpeedLimitReached] = false - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - else - stateFlags[:error] = true - end - - return (CS, drivingCourse, stateFlags) -end #function addClearingSection - -## This function calculates the data points of the accelerating section. - # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section -function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) - #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) - CSs = movingSection[:characteristicSections] - CS = CSs[csId] - drivingCourse = movingSection[:drivingCourse]=# - - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for the accelerating section - targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] || stateFlags[:speedLimitReached] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - previousSpeedLimitReached = stateFlags[:previousSpeedLimitReached] - - # use the conditions for the accelerating section - if !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached - BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit] - #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 - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - 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 - - while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached - # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit - if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] - # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - 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])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # conditions for the next while cycle - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - speedLimitReached = drivingCourse[end][:v] > CS[:v_limit] - previousSpeedLimitReached = currentSpeedLimit[:v] < CS[:v_limit] && (drivingCourse[end][:v] > currentSpeedLimit[:v] || (drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end])) - targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] - #targetSpeedReached = speedLimitReached - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well - tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] - end #while - - if CS[:id]==0 - testFlag = true - else - testFlag = false # for testing - end - - # check which limit was reached and adjust the currentStepSize for the next cycle - 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 - - 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 - - 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 == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - 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 == :speed - currentStepSize = CS[:v_peak]-drivingCourse[end-1][:v] - else - 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 == :velocity - currentStepSize = currentSpeedLimit[:v]-drivingCourse[end-1][:v] - - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - 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 - end - break - - 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 - break - - elseif drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing - if nextPointOfInterest == CS[:s_exit] - endOfCSReached = true - end - break - - else - println("v=",drivingCourse[end][:v]," v_peak= ", CS[:v_peak] , " v_cLimit=", currentSpeedLimit[:v]) - 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 CS[:v_peak] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=",CS[:v_peak]) # for testing - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next section - brakingStartReached = false - - 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 - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - else - drivingCourse[end][:s] = CS[:s_exit] # round s down to CS[:s_exit] - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - end - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - - elseif 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 - - 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 - previousSpeedLimitReached = true - - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - else - if drivingCourse[end][:s] + s_braking == CS[:s_exit] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," else case and there: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - 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 - - end - end - - # TODO is it possible to put this into to the if-fork? - if drivingCourse[end][:s] == CS[:s_exit] - endOfCSReached = true - end - - end - end #for - - if drivingCourse[end][:s] == CS[:s_exit] - endOfCSReached = true - end - - end #while - - if length(BS[:dataPoints]) > 1 - # calculate the accumulated accelerating section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - # 03/10 old: CS[:v_peak] = max(drivingCourse[end][:v], CS[:v_entry]) # setting v_peak to the last data points velocity which is the highest reachable value in this characteristic section or to v_entry in case it is higher when running on a path with high resistances - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = !(tractionSurplus || drivingCourse[end][:F_T] == drivingCourse[end][:F_R]) # or add another flag for equal forces? - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:previousSpeedLimitReached] = previousSpeedLimitReached - stateFlags[:speedLimitReached] = targetSpeedReached - stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) - - return (CS, drivingCourse, stateFlags) -end #function addAcceleratingSection! - - -## This function calculates the data points of the cruising section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed. -function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Dict, CSs::Vector{Dict}, cruisingType::String) - trainIsClearing = cruisingType == "clearing" - trainIsBrakingDownhill = cruisingType == "downhillBraking" - - # traction effort and resisting forces (in N) - if !trainIsBrakingDownhill # TODO: or just give BS[:type] instead of "cruising"/"braking"? - calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - else - calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - end - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for cruising section - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - targetPositionReached = s_cruising == 0.0 - resistingForceNegative = drivingCourse[end][:F_R] < 0 -#println(" vor if speedIsValid=",speedIsValid ," brakingStartReached=", brakingStartReached," tractionDeficit=", tractionDeficit," targetPositionReached=", targetPositionReached) - - if speedIsValid && !brakingStartReached && !tractionDeficit && !targetPositionReached - # 03/04 old: if drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] && !brakingStartReached && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - BS = createBehaviorSection(cruisingType, drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - # TODO: necessary? - s_cruising = min(s_cruising, CS[:s_exit]-BS[:s_entry]) - - # traction effort and resisting forces (in N) -#03/25 calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - if !trainIsBrakingDownhill - calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - else - calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - end - - 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 - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 -# targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak] - # TODO: change? to correctCruisingType = (trainIsClearing || (trainIsBrakingDownhill == drivingCourse[end][:F_R] < 0)) # while clearing tractive or braking force can be used -#&& 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 - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - 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 - #TODO: maybe just consider former CS with different path resistance? - # tractive effort (in N): -#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - if !trainIsBrakingDownhill - drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - else - drivingCourse[end][:F_T] = 0.0 - end - - - # acceleration (in m/s^2): - drivingCourse[end][:a] = 0.0 - - # create the next data point - if settings.stepVariable == :distance || settings.stepVariable == time - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - else - push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? - 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) - #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - #end - - # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - end #while - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - currentStepSize = settings.stepSize / 10.0^cycle - - elseif !trainIsBrakingDownhill && resistingForceNegative - currentStepSize = settings.stepSize / 10.0^cycle - - elseif trainIsBrakingDownhill && !resistingForceNegative - currentStepSize = settings.stepSize / 10.0^cycle - - elseif drivingCourse[end][:s] > nextPointOfInterest - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - 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 == :distance - currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] - break - - elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - break - - elseif !trainInPreviousCS - 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") - end - - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - pointOfInterestReached = false - tractionDeficit = false - targetPositionReached = false - trainInPreviousCS = true - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - - else # if the level of approximation is reached - if drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - elseif drivingCourse[end][:s] > BS[:s_entry]+s_cruising - if BS[:type] != "clearing" - pop!(drivingCourse) - pop!(BS[:dataPoints]) - end - elseif drivingCourse[end][:s] == BS[:s_entry]+s_cruising - break - elseif drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - break - elseif !trainIsBrakingDownhill && resistingForceNegative - break - elseif trainIsBrakingDownhill && !resistingForceNegative - break - elseif !trainInPreviousCS - break - - else - - end - end - end #for - end #while - end #if - - # conditions for the next while cycle - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - - while !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - # 03/09 old: while drivingCourse[end][:s] < BS[:s_entry]+s_cruising && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])) - - # tractive effort (in N): -#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - if !trainIsBrakingDownhill - drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - else - drivingCourse[end][:F_T] = 0.0 - end - - - - drivingCourse[end][:a] = 0.0 # acceleration (in m/s^2) - - # calculate the remaining cruising way - #s_cruisingRemaining=BS[:s_entry] + s_cruising-drivingCourse[end][:s] - 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], :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) - #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - #end - - # conditions for the next while cycle - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - resistingForceNegative = drivingCourse[end][:F_R] < 0 - end #while - - # TODO: realize this better inside the upper loops? - - - # calculate the accumulated cruising section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end # else: return the characteristic section without a cruising section - - # set state flags - stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit] - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit] - stateFlags[:tractionDeficit] = tractionDeficit - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0 - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative))) - - return (CS, drivingCourse, stateFlags) -end #function addCruisingSection! - - -## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower -function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for diminishing section - targetSpeedReached = drivingCourse[end][:v] <= 0.0 - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit] - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - - # use the conditions for the diminishing section - if tractionDeficit && !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("diminishing", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached - 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: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])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # conditions for the next while cycle - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - targetSpeedReached = drivingCourse[end][:v] <= 0.0 - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - endOfCSReached = drivingCourse[end][:s] == CS[:s_exit] - end #while - - if CS[:id]==0 - testFlag = true - else - testFlag = false # for testing - end - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:v] < 0.0 - if settings.stepVariable == velocity - currentStepSize = drivingCourse[end-1][:v] - else - 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 - - 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 - - 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 == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif 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 - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing - break - - 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 - break - - elseif drivingCourse[end][:v] == 0.0 - error("ERROR: The train stops during diminishing run in CS",CS[:id]," at position s=",drivingCourse[end][:s]," m because the maximum tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", - " 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") - end - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - brakingStartReached = false - pointOfInterestReached = false - targetSpeedReached = false - tractionDeficit = true - endOfCSReached = false - - else # if the level of approximation is reached - if drivingCourse[end][:v] <= 0.0 - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: v=", drivingCourse[end][:v]," <= 0.0") # for testing - # push!(BS[:dataPoints], drivingCourse[end][:i]) - error("ERROR: The train stops during diminishing run in CS",CS[:id]," because the maximum tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", - " 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.") - - 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 - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - pointOfInterestReached = false - targetSpeedReached = false - tractionDeficit = true - endOfCSReached = false - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - - 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 - break - - else - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: else with v=", drivingCourse[end][:v]," > 0.0 and F_T=", drivingCourse[end][:F_T]," <= F_R=", drivingCourse[end][:F_R]) # for testing - #println(" and s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," <= s_exit=",CS[:s_exit]) # for testing - #println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest=",nextPointOfInterest) # for testing - - # if drivingCourse[end][:s] + s_braking == CS[:s_exit] - # brakingStartReached = true - # end - end #if - - # # TODO is it possible to put this into to the if-fork? - # if drivingCourse[end][:s] == CS[:s_exit] - # endOfCSReached = true - # end - end #if - end #for - end #while - - if length(BS[:dataPoints]) > 1 # TODO: necessary? May it be possible that there is no diminishing because braking has to start? - # calculate the accumulated diminishing section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = tractionDeficit - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_peak] - stateFlags[:error] = !(endOfCSReached || brakingStartReached || !tractionDeficit) - - return (CS, drivingCourse, stateFlags) -end #function addDiminishingSection! - - -## This function calculates the data points of the coasting section. -# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section -function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # 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 - - # conditions for coasting section - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - - # use the conditions for the coasting section - if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("coasting", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while !targetSpeedReached && !endOfCSReached && !brakingStartReached - 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: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) - - # 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])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - # conditions for the next while cycle - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] - end # while - - testFlag = false - - # check which limit was reached and adjust the currentStepSize for the next cycle - 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 - - 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 == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - 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 == velocity - currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] - else - 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 == velocity - currentStepSize = CS[:v_peak] - drivingCourse[end-1][:v] - else - 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 - break - - elseif drivingCourse[end][:v] == CS[:v_exit] - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," == v_exit=", CS[:v_exit]) # for testing - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - break - - 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] - # delete last data point because it went to far - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - # brakingStartReached = true - pointOfInterestReached = false - targetSpeedReached = false - - elseif drivingCourse[end][:v] > CS[:v_peak] # if the train gets to fast it has to brake # TODO: if accelereation and coasting functions will be combined this case is different for coasting and also the order of if cases is different - # delete last data point because it went to far - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - brakingStartReached = false - pointOfInterestReached = false - # targetSpeedReached = true - - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - else - # do nothing for example for drivingCourse[end][:s] + s_braking == CS[:s_exit] - end - end - end #for - end #while - - stateFlags[:speedLimitReached] = false - - # calculate the accumulated coasting section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:coasting=>BS)) - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) - - return (CS, drivingCourse, stateFlags) -end #function addCoastingSection! - - -## This function calculates the data points of the braking section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. -function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # conditions for braking section - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - - # use the conditions for the braking section - if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while !targetSpeedReached && !endOfCSReached - 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: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) - - # 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 == :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 - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - else - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - end - #println(drivingCourse[end][:i],". s=",drivingCourse[end][:s]," s_exit=", CS[:s_exit]," v_exit=", CS[:v_exit]," v=",drivingCourse[end][:v]) - - # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - end # while - - # 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 < settings.approxLevel+1 - if drivingCourse[end][:v] < CS[:v_exit] - if settings.stepVariable == velocity - currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:s] > nextPointOfInterest - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] - break - elseif drivingCourse[end][:v] == CS[:v_exit] - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - # println(" with a=", drivingCourse[end-1][:a]) # for testing - break - elseif drivingCourse[end][:s] == CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - targetSpeedReached = true - # println(" with a=", drivingCourse[end-1][:a]) # for testing - break - elseif drivingCourse[end][:s] == nextPointOfInterest - break - end - - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - pointOfInterestReached = false - endOfCSReached = false - targetSpeedReached = false - - else # if the level of approximation is reached - if drivingCourse[end][:v] < 0.0 - # TODO: drivingCourse[end][:v] < CS[:v_exit] should be enough - # reset last point with setting v=v_exit - # println("during braking section in CS",CS[:id],": rounding v up from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], 0.0) - endOfCSReached = true - break - elseif drivingCourse[end][:s] > CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding s down from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - # recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - drivingCourse[end][:s] = CS[:s_exit] - break - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - break - elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] - break - elseif drivingCourse[end][:v] < CS[:v_exit] - # reset last point with setting v=v_exit - # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - break - elseif drivingCourse[end][:v] == CS[:v_exit] - # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - break - elseif drivingCourse[end][:s] == CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - targetSpeedReached = true - break - else - # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest - end - end - end #for - end #while - - # calculate the accumulated coasting section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:braking=>BS)) - end # else: return the characteristic section without a braking section - - # set state flags - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:error] = !(endOfCSReached) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - - return (CS, drivingCourse, stateFlags) -end #function addBrakingSection! - - -## This function calculates the data point of the standstill. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. -function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) - 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) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :s_exit => drivingCourse[end][:s], # last position (in m) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - drivingCourse[end][:behavior] = BS[:type] - - # traction effort and resisting forces (in N) - 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 - return (CS, drivingCourse) -end #function addStandstill! - -function mergeBehaviorSection!(BSs::Dict, BS::Dict) - if !haskey(BSs, Symbol(BS[:type])) - merge!(BSs, Dict(Symbol(BS[:type]) => BS)) - else - number = "2" - while haskey(BSs, Symbol(BS[:type]*number)) - number = string(parse(Int, number)+1) - end - merge!(BSs, Dict(Symbol(BS[:type]*number) => BS)) - # println("INFO: The ",number,". ",BS[:type]," section has been created. ! ! ! ! ! ! ! ! !") - end - return BSs -end #function mergeBehaviorSection! - -function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) - BS= Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill - :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" - :length => 0.0, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => 0.0, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_entry => v_entry, # entry speed (in m/s) - :v_exit => 0.0, # exit speed (in m/s) - :dataPoints => [startingPoint]) # list of identifiers of the containing data points starting with the initial point - return BS -end #function createBehaviorSection - -""" -a data point is the smallest element of the driving course. One step of the step approach is between two data points -""" -function createDataPoint() - dataPoint = Dict(:i => 0, # identifier and counter variable of the dricing course - :behavior => "", # type of behavior section the data point is part of ("breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") - # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter - :s => 0.0, # position (in m) - :Δs => 0.0, # step size (in m) - :t => 0.0, # point in time (in s) - :Δt => 0.0, # step size (in s) - :v => 0.0, # velocity (in m/s) - :Δv => 0.0, # step size (in m/s) - :a => 0.0, # acceleration (in m/s^2) - :W => 0.0, # mechanical work (in Ws) - :ΔW => 0.0, # mechanical work in this step (in Ws) - :E => 0.0, # energy consumption (in Ws) - :ΔE => 0.0, # energy consumption in this step (in Ws) - :F_T => 0.0, # tractive effort (in N) - :F_R => 0.0, # resisting force (in N) - :R_path => 0.0, # path resistance (in N) - :R_train => 0.0, # train resistance (in N) - :R_traction => 0.0, # traction unit resistance (in N) - :R_wagons => 0.0) # set of wagons resistance (in N) - return dataPoint -end #function createDataPoint - -function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) - currentPoint = drivingCourse[end] - previousPoint = drivingCourse[end-1] - # set s and v - currentPoint[:s] = s_target # position (in m) - currentPoint[:v] = v_target # velocity (in m/s) - currentPoint[:Δs] = currentPoint[:s] - previousPoint[:s] # step size (in m) - currentPoint[:Δv] = currentPoint[:v] - previousPoint[:v] # step size (in m/s) - - # calculate other values - previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs]) -# # TODO: just for testing -# if previousPoint[:a]=0.0 -# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train[:a_braking]) -# end - currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s) - currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s) - - currentPoint[:ΔW] = 0.0 # mechanical work in this step (in Ws) - currentPoint[:W] = previousPoint[:W] + currentPoint[:ΔW] # mechanical work (in Ws) - currentPoint[:ΔE] = currentPoint[:ΔW] # energy consumption in this step (in Ws) - currentPoint[:E] = previousPoint[:E] + currentPoint[:ΔE] # energy consumption (in Ws) -end #function recalculateLastBrakingPoint diff --git a/src/Calc.jl b/src/Calc.jl deleted file mode 100644 index 3ba2662..0000000 --- a/src/Calc.jl +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -# 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::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) -todo !!! -``` -""" -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.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) - train = copy(trainInput) - path = copy(pathInput) - - # check the input data - (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.outputDetail == :everything && println("The moving section has been prepared.") - - # calculate the train run for oparation mode "minimum running time" - (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) - - return output -end # function trainRun - -# calculate a train run focussing on using the minimum possible running time -function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) - CSs::Vector{Dict} = movingSection[:characteristicSections] - - 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) - drivingCourse::Vector{Dict} = [startingPoint] # List of data points - - for csId in 1:length(CSs) - CS = CSs[csId] - # for testing - if drivingCourse[end][:s] != CS[:s_entry] - println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry]) - end - if drivingCourse[end][:v] > CS[:v_entry] - println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry]) - end - - # determine the different flags for switching between the states for creatinge moving phases - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - 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], - :brakingStartReached => drivingCourse[end][:s] + s_braking == CS[:s_exit], - :tractionDeficit => drivingCourse[end][:F_T] < drivingCourse[end][:F_R], # or add another flag for equal forces? - :resistingForceNegative => drivingCourse[end][:F_R] < 0.0, - :previousSpeedLimitReached => false, #speedLimitReached, # check already at this position? - :speedLimitReached => drivingCourse[end][:v] > CS[:v_limit], - :error => false) - - # determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") - while !stateFlags[:endOfCSReached] # s < s_exit - if !stateFlags[:brakingStartReached] # s+s_braking < s_exit - if !stateFlags[:tractionDeficit] - if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0 - (CS, drivingCourse, stateFlags) = addBreakFreeSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif stateFlags[:previousSpeedLimitReached] - (CS, drivingCourse, stateFlags) = addClearingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] - (CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] - # cruise only one step - 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") - - elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking - - if s_cruising > 0.0 - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") - else - stateFlags[:brakingStartReached] = true - end - - elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking - - if s_cruising > 0.0 # TODO: define a minimum cruising length? - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") - else - stateFlags[:brakingStartReached] = true - end - else - error() - end - elseif stateFlags[:tractionDeficit] - (CS, drivingCourse, stateFlags) = addDiminishingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - else - error() - end - else#if !stateFlags[:endOfCSReached] # s < s_exit - (CS, drivingCourse, stateFlags) = addBrakingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - #else - # error() - end - end - #if s == s_exit - # standstill - #end - - - # for testing: - if drivingCourse[end][:s] != CS[:s_exit] - println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit]) - end - if drivingCourse[end][:v] > CS[:v_exit] - println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit]) - end - end #for - - (CSs[end], drivingCourse) = addStandstill!(CSs[end], drivingCourse, settings, train, CSs) - - movingSection[:t] = drivingCourse[end][:t] # total running time (in s) - movingSection[:E] = drivingCourse[end][:E] # total energy consumption (in Ws) - - return (movingSection, drivingCourse) -end #function calculateMinimumRunningTime diff --git a/src/Characteristics.jl b/src/Characteristics.jl deleted file mode 100644 index 4dd83c2..0000000 --- a/src/Characteristics.jl +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior -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) - #movingSection = secureCruisingBehavior!(movingSection, settings, train) - - return movingSection -end #function determineCharacteristics - -## create a moving section containing characteristic sections -function createMovingSection(path::Dict, v_trainLimit::Real) - # this function creates and returns a moving section dependent on the paths attributes - - s_entry = path[:sections][1][:s_start] # first position (in m) - s_exit = path[:sections][end][:s_end] # last position (in m) - pathLength = s_exit - s_entry # total length (in m) - - CSs=Vector{Dict}() - s_csStart=s_entry - csId=1 - for row in 2:length(path[:sections]) - previousSection = path[:sections][row-1] - currentSection = path[:sections][row] - speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) - pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] - if speedLimitIsDifferent || pathResistanceIsDifferent - # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] - push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), path)) - s_csStart = currentSection[:s_start] - csId = csId+1 - end #if - end #for - push!(CSs, createCharacteristicSection(csId, s_csStart, path[:sections][end], min(path[:sections][end][:v_limit], v_trainLimit), path)) - - movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore - :length => pathLength, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => s_exit, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :characteristicSections => CSs) # list of containing characteristic sections - - return movingSection -end #function createMovingSection - - -## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. -function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, path::Dict) - # Create and return a characteristic section dependent on the paths attributes - characteristicSection= Dict(:id => id, # identifier - :s_entry => s_entry, # first position (in m) - :s_exit => section[:s_end], # last position (in m) - :length => section[:s_end] -s_entry, # total length (in m) - :r_path => section[:f_Rp], # path resistance (in ‰) - :behaviorSections => Dict(), # list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => v_limit, # speed limit (in m/s) - # initializing :v_entry, :v_peak and :v_exit with :v_limit - :v_peak => v_limit, # maximum reachable speed (in m/s) - :v_entry => v_limit, # maximum entry speed (in m/s) - :v_exit => v_limit) # maximum exit speed (in m/s) - - # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated - s_exit = characteristicSection[:s_exit] - pointsOfInterest = Vector{Real}() - if haskey(path, :pointsOfInterest) - for POI in path[:pointsOfInterest] - if s_entry < POI && POI < s_exit - push!(pointsOfInterest, POI) - end - end - end - push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with - - merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) - - return characteristicSection -end #function createCharacteristicSection - -## define the intersection velocities between the characterisitc sections to secure braking behavior -function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) - # this function limits the entry and exit velocity of the characteristic sections to secure that the train stops at the moving sections end - CSs = movingSection[:characteristicSections] - - csId = length(CSs) - followingCSv_entry = 0.0 # the exit velocity of the last characteristic section is 0.0 m/s - while csId >= 1 - CS = CSs[csId] - - CS[:v_exit] = min(CS[:v_limit], followingCSv_entry) - - v_entryMax = calcBrakingStartVelocity(CS[:v_exit], a_braking, CS[:length]) - - CS[:v_entry] = min(CS[:v_limit], v_entryMax) - CS[:v_peak] = CS[:v_entry] - - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - - followingCSv_entry = CS[:v_entry] - csId = csId - 1 - end #while - return movingSection -end #function secureBrakingBehavior! - -## define the intersection velocities between the characterisitc sections to secure accelerating behavior -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] - - CSs[1][:v_entry] = 0.0 # the entry velocity of the first characteristic section is 0.0 m/s - startingPoint = createDataPoint() - startingPoint[:i] = 1 - - previousCSv_exit = CSs[1][:v_entry] - for CS in CSs - 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) - acceleratingCourse::Vector{Dict} = [startingPoint] # List of data points - - if CS[:v_entry] < CS[:v_peak] - # conditions for entering the accelerating phase - stateFlags = Dict(:endOfCSReached => false, - :brakingStartReached => false, - :tractionDeficit => false, - :resistingForceNegative => false, - :previousSpeedLimitReached => false, - :speedLimitReached => false, - :error => false, - :usedForDefiningCharacteristics => true) # because usedForDefiningCharacteristics == true the braking distance will be ignored during securing the accelerating phase - v_peak = CS[:v_entry] - (CS, acceleratingCourse, stateFlags) = addBreakFreeSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) - while !stateFlags[:speedLimitReached] && !stateFlags[:endOfCSReached] - if !stateFlags[:tractionDeficit] - if !stateFlags[:previousSpeedLimitReached] - (CS, acceleratingCourse, stateFlags) = addAcceleratingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function changes the acceleratingCourse - - elseif stateFlags[:previousSpeedLimitReached] - (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] - 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 - end - end - v_peak = max(v_peak, acceleratingCourse[end][:v]) - end - -# CS[:v_peak] = max(CS[:v_entry], acceleratingCourse[end][:v]) - CS[:v_peak] = v_peak - CS[:v_exit] = min(CS[:v_exit], CS[:v_peak], acceleratingCourse[end][:v]) - else #CS[:v_entry] == CS[:v_peak] - # v_exit stays the same - end #if - - previousCSv_exit = CS[:v_exit] - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - end #for - - return movingSection -end #function secureAcceleratingBehavior! - - -#= -## define the intersection velocities between the characterisitc sections to secure cruising behavior -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] - - startingPoint = createDataPoint() - startingPoint[:i] = 1 - - previousCSv_exit = CSs[1][:v_entry] - - for CS in CSs - # conditions for entering the cruising phase - stateFlags = Dict(:endOfCSReached => false, - :brakingStartReached => false, - :tractionDeficit => false, - :resistingForceNegative => false, - :previousSpeedLimitReached => false, - :speedLimitReached => false, - :error => false, - :usedForDefiningCharacteristics => true) - - CS[:v_entry] = min(CS[:v_entry], previousCSv_exit) - - startingPoint[:s] = CS[:s_entry] - startingPoint[:v] = CS[:v_peak] - cruisingCourse::Vector{Dict} = [startingPoint] # List of data points - - while !stateFlags[:endOfCSReached] #&& s_cruising > 0.0 - if !stateFlags[:tractionDeficit] - s_cruising = CS[:s_exit] - cruisingCourse[end][:s] - if !stateFlags[:resistingForceNegative]# cruisingCourse[end][:F_R] >= 0 - (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") # this function changes the cruisingCourse - else - (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] - 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 - end - end - end - - CS[:v_exit] = min(CS[:v_exit], cruisingCourse[end][:v]) - - previousCSv_exit = CS[:v_exit] - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - end #for - - return movingSection -end #function secureCruisingBehavior! -=# diff --git a/src/Export.jl b/src/Export.jl deleted file mode 100644 index 7113866..0000000 --- a/src/Export.jl +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -function exportToCsv(runningTime::AbstractFloat, settings::Settings) - createCsvFile(runningTime, settings) - - return true -end - -function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Settings) - createCsvFile(dataPointsToExport, settings) - - return true -end - -function exportToCsv(output::Dict) - 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][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumRunningTime] - else - dataPointsToExport = output[:drivingCourseMinimumRunningTime] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - if output[:settings][:operationModeMinimumEnergyConsumption] == true - operationMode = "minimum energy consumption" - if output[:settings][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumEnergyConsumption] - else - dataPointsToExport = output[:drivingCourseMinimumEnergyConsumption] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - return true - end - return false -end #function exportToCsv - -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 outputDir - date = Dates.now() - dateString = Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - - csvFilePath = settings[:outputDir]*"/"*dateString*"_RunningTime.csv" - - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -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] - - allColumns = Array{Any,1}[] - for column in 1:length(header) - currentColumn = Any[] - push!(currentColumn, header[column]) - for point in dataPointsToExport - push!(currentColumn, point[columnSymbols[column]]) - end - push!(allColumns, currentColumn) - end # for - - - # 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 - println("") - end - - date = Dates.now() - dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - csvFilePath=settings[:outputDir]*"/"*dateString*"_DataPoints.csv" - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -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) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - 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]] - push!(accumulatedData, row) # push row to accumulatedData - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile - - - -#= -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) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - 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 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]] - push!(accumulatedData, row) # push row to accumulatedData - end - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # 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 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile -=# diff --git a/src/Formulary.jl b/src/Formulary.jl deleted file mode 100644 index 1be57a6..0000000 --- a/src/Formulary.jl +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2022" -# __license__ = "ISC" - -######################### -## literature the driving dynamics equations are based on: -## -## @incollection{Bruenger:2014, % Chapter 4 -## author = {Brünger, Olaf and Dahlhaus, Elias}, -## year = {2014}, -## title = {Running Time Estimation}, -## pages = {65--90}, -## booktitle = {Railway Timetabling \& Operations.}, -## editora = {Hansen, Ingo A.}, -## editorb = {Pachl, Jörn}, -## isbn = {978-3-777-10462-1}, -## publisher = {Eurailpress DVV Media Group}, -## } -## @Book{Wende:2003, -## author = {Wende, Dietrich}, -## date = {2003}, -## title = {Fahrdynamik des Schienenverkehrs}, -## isbn = {978-3-322-82961-0}, -## publisher = {Springer-Verlag}, -## } -######################### - -approxLevel = 6 -v00 = 100/3.6 # velocity factor (in m/s) -g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2 - -## calculate forces - -#TODO: replace the ? ? ? -""" - calcTractionUnitResistance(v, train) - -Calculate the vehicle resistance for the traction unit of the `train` dependend on the velocity `v`. - -... -# Arguments -- `v::AbstractFloat`: the current velocity in m/s. -- `train::Dict`: ? ? ? -... - -# Examples -```julia-repl -julia> calcTractionUnitResistance(30.0, ? ? ?) -? ? ? -``` -""" -function calcTractionUnitResistance(v::AbstractFloat, train::Dict) - # equation is based on [Wende:2003, page 151] - f_Rtd0 = train[:f_Rtd0] # coefficient for basic resistance due to the traction units driving axles (in ‰) - f_Rtc0 = train[:f_Rtc0] # coefficient for basic resistance due to the traction units carring axles (in ‰) - F_Rt2 = train[:F_Rt2] # coefficient for air resistance of the traction units (in N) - m_td = train[:m_td] # mass on the traction unit's driving axles (in kg) - m_tc = train[:m_tc] # mass on the traction unit's carrying axles (in kg) - Δv_t = train[:Δv_t] # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - - F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ - # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) - return F_R_tractionUnit - #TODO: same variable name like in the rest of the tool? return R_traction - #TODO: just one line? return train[:f_Rtd0]/1000*train[:m_td]*g+train[:f_Rtc0]/1000*train[:m_tc]*g+train[:F_Rt2]*((v+train[:Δv_t])/v00)^2 # /1000 because of the unit ‰ -end #function calcTractionUnitResistance - -""" -TODO -calculate and return the wagons vehicle resistance dependend on the velocity -""" -function calcWagonsResistance(v::AbstractFloat, train::Dict) - # equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135) - f_Rw0 = train[:f_Rw0] # coefficient for basic resistance of the set of wagons (consist) (in ‰) - f_Rw1 = train[:f_Rw1] # coefficient for the consists resistance to rolling (in ‰) - f_Rw2 = train[:f_Rw2] # coefficient fo the consistsr air resistance (in ‰) - m_w = train[:m_w] # mass of the set of wagons (consist) (in kg) - Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - - F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_w) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ -# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_w) /v00)^2 # vehicle resistance of the wagons (in N) - return F_R_wagons -end #function calcWagonsResistance - -function calcForceFromCoefficient(f_R::Real, m::Real) - # equation is based on [Wende:2003, page 8] - - # f_R: specific resistance (in ‰) - # m: vehicle's mass (in kg) - - F_R = f_R /1000 *m *g # Resisting Force (in N) # /1000 because of the unit ‰ - return F_R -end #function calcForceFromCoefficient - -function calcAcceleration(F_T::Real, F_R::Real, m_train::Real, ξ_train::Real) - # equation is based on [Bruenger:2014, page 72] with a=dv/dt - - # F_T: tractive effort (in N) - # F_R: resisting forces (in N) - # m_train: train's mass (in kg) - # ξ_train: train's rotation mass factor (without unit) - - a = (F_T - F_R) /m_train /ξ_train # acceleration (in m/s) - return a -end #function calcAcceleration - -function calc_Δs_with_Δt(Δt::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δt: time step (in s) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δs = Δt * (2*v_prev + Δt*a_prev) /2 # step size (in m) - return Δs -end #function calc_Δs_with_Δt - -function calc_Δs_with_Δv(Δv::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δv: velocity step (in m/s) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δs = ((v_prev + Δv)^2 - v_prev^2)/2/a_prev # step size (in m) - return Δs -end #function calc_Δs_with_Δv - -function calc_Δt_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - - Δt = sign(a_prev) *sqrt((v_prev /a_prev)^2 + 2 *Δs /a_prev) - v_prev /a_prev # step size (in m/s) - return Δt -end #function calc_Δt_with_Δs - -function calc_Δt_with_Δv(Δv::Real, a_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δv: velocity step (in m/s) - # a_prev: acceleration from previous data point - Δt = Δv /a_prev # step size (in s) - return Δt -end #function calc_Δt_with_Δv - -function calc_Δt_with_constant_v(Δs::Real, v::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # v: constant velocity (in m/s) - Δt = Δs /v # step size (in s) - return Δt -end #function calc_Δt_with_constant_v - -function calc_Δv_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δv = sqrt(v_prev^2 + 2*Δs*a_prev) - v_prev # step size (in m/s) - return Δv -end #function calc_Δv_with_Δs - -function calc_Δv_with_Δt(Δt::Real, a_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δt: time step (in s) - # a_prev: acceleration from previous data point - Δv = Δt * a_prev # step size (in m/s) - return Δv -end #function calc_Δv_with_Δt - -function calc_ΔW(F_T_prev::Real, Δs::Real) - # equation is based on [Wende:2003, page 17] - - # F_T_prev: tractive force from previous data point - # Δs: distance step - ΔW = F_T_prev * Δs # mechanical work in this step (in Ws) - return ΔW -end #function calc_ΔW - -function calc_ΔE(ΔW::Real) - # simplified equation - # TODO! - # ΔW: mechanical work in this step (in Ws) - ΔE = ΔW # energy consumption in this step (in Ws) - return ΔE -end #function calc_ΔW - -function calcBrakingDistance(v_start::Real, v_end::Real, a_braking::Real) - # equation is based on [Wende:2003, page 37] - - # v_start: velocity at the start of braking (in m/s) - # v_end: target velocity at the end of braking (in m/s) - # 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=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) - # equation is based on [Wende:2003, page 37] - - # v_end: target velocity at the end of braking (in m/s) - # 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=approxLevel) - return floor(v_start, digits=approxLevel +1) -end #function calcBrakingStartVelocity - -function calcBrakingAcceleration(v_start::Real, v_end::Real, s_braking::Real) - # equation is based on [Wende:2003, page 37] - - # v_start: braking start velocity (in m/s) - # v_end: target velocity at the end of braking (in m/s) - # s_braking: braking distance (in Ws) - a_braking = (v_end^2 - v_start^2) /2 /s_braking # constant braking acceleration (in m/s^2) - return a_braking -end #function calcBrakingAcceleration diff --git a/src/Import.jl b/src/Import.jl deleted file mode 100644 index aae95af..0000000 --- a/src/Import.jl +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -""" -Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. -""" -function importYamlFiles(trainDirectory::String, pathDirectory::String) - train = importFromYaml(:train, trainDirectory) - path = importFromYaml(:path, pathDirectory) - - return (train, path) -end #function importYamlFiles - - """ - Read the train information from a YAML file, save it in a Dict and return it. - """ -function importFromYaml(dataType::Symbol, directory::String) - dataSet = String(dataType) - data = YAML.load(open(directory)) - if collect(keys(data))[1] != dataSet - error("ERROR at reading the ", dataSet, " yaml file: The data set is called ", collect(keys(data))[1]," and not ", dataSet, ".") - end - dataKeys = collect(keys(data[dataSet])) - dataKeys = collect(keys(data[dataSet])) - dataValues = collect(values(data[dataSet])) - dictionary = Dict() - for number in 1:length(dataKeys) - merge!(dictionary, Dict(Symbol(dataKeys[number]) => dataValues[number])) - end - return dictionary -end # function importFromYaml diff --git a/src/Output.jl b/src/Output.jl deleted file mode 100644 index a21c12e..0000000 --- a/src/Output.jl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -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.outputDetail == :points_of_interest - # add points of interest - if haskey(path, :pointsOfInterest) - output = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(output, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - end - - elseif settings.outputDetail == :driving_course - output = drivingCourse - - elseif settings.outputDetail == :everything - output = Dict{Symbol,Any}() - merge!(output, Dict(:train => train, :path => path, :settings => settings)) - - - # add moving section and driving courses - if settings[:operationModeMinimumRunningTime] == true - merge!(output, Dict(:movingSectionMinimumRunningTime => movingSection, - :drivingCourseMinimumRunningTime => drivingCourse)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(output, Dict(:movingSectionMinimumEnergyConsumption => movingSection, - :drivingCourseMinimumEnergyConsumption => drivingCourse)) - end - - # add points of interest - if haskey(path, :pointsOfInterest) - pointsOfInterest = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(pointsOfInterest, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - - if settings[:operationModeMinimumRunningTime] == true - merge!(output, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(output, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) - end - end - else - output = nothing - end - return output -end - -#= -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)) - - - # add moving section and driving courses - if settings[:operationModeMinimumRunningTime] == true - merge!(outputDict, Dict(:movingSectionMinimumRunningTime => movingSection, - :drivingCourseMinimumRunningTime => drivingCourse)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(outputDict, Dict(:movingSectionMinimumEnergyConsumption => movingSection, - :drivingCourseMinimumEnergyConsumption => drivingCourse)) - end - - # add points of interest - if haskey(path, :pointsOfInterest) - pointsOfInterest = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(pointsOfInterest, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - - if settings[:operationModeMinimumRunningTime] == true - merge!(outputDict, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(outputDict, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) - end - end - - return outputDict -end # function createOutputDict -=# diff --git a/src/Types.jl b/src/Types.jl deleted file mode 100644 index 3419b44..0000000 --- a/src/Types.jl +++ /dev/null @@ -1,799 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg, Martin Scheidt" -# __copyright__ = "2022" -# __license__ = "ISC" - -""" - Settings(file) - -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 - - 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::Settings) - checkAndSetTrain!(train) - checkAndSetPath!(path) - - 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! - -""" -Read the train information from a YAML file, save it in a train Dict and return it. -""" -function checkAndSetTrain!(train::Dict) - # check train information from input dictionary - - checkAndSetString!(train, "train", :name, "") # train's name - # add train's identifier if not existing - if !(haskey(train, :id) && train[:id]!=nothing) - merge!(train, Dict(:id =>1)) - end - checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight" - - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) - # TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) - - checkAndSetSpeedLimit!(train) # train's speed limit (in m/s) - checkAndSetBrakingAcceleration!(train) # a_braking - - checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg) - checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg) - checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg) - - checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg) - checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg) - if train[:m_train] <= 0.0 - error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.") - end - - checkAndSetRotationMassFactors!(train) - checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort - - # coefficients for the vehicle resistance of the traction unit - checkAndSetRealNumber!(train, "train", :Δv_t, "m/s", 15.0/3.6) # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rtd0, "‰", 0.0) # coefficient for basic resistance due to the traction units driving axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rtc0, "‰", 0.0) # coefficient for basic resistance due to the traction units carring axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :F_Rt2, "N", 0.0) # coefficient for air resistance of the traction units (in N) - - # coefficients for the vehicle resistance of the set of wagons (consist) - checkAndSetRealNumber!(train, "train", :Δv_w, "m/s", getDefault_Δv_w(train[:type])) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rw0, "‰", 0.0) # coefficient for basic resistance of the set of wagons (consist) (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw1, "‰", 0.0) # coefficient for the consists resistance to rolling (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw2, "‰", 0.0) # coefficient fo the consistsr air resistance (in ‰) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, :id, :type, - :length, :l_train, :v_limit, :v_limit_kmh, :a_braking, - :m_train, :m_t, :m_td, :m_tc, :m_w, - :ξ_train, :ξ_t, :ξ_w, :rotationMassFactor_train, :rotationMassFactor_t, :rotationMassFactor_w, - :tractiveEffortVelocityPairs, :F_T_pairs, :F_T_pairs_kmh, - :f_Rtd0, :f_Rtc0, :F_Rt2, :Δv_t, - :f_Rw0, :f_Rw1, :f_Rw2, :Δv_w] - informAboutUnusedKeys(collect(keys(train)), usedKeys::Vector{Symbol}, "train") - - return train -end #function checkAndSetTrain! - -function checkAndSetPath!(path::Dict) - # check path information from input dictionary - - checkAndSetString!(path, "path", :name, "") - # TODO checkId ? path[:id] # path identifier - checkAndSetSections!(path) - checkAndSetPOIs!(path) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, - :sections, :sectionStarts, :sectionStarts_kmh, - :pointsOfInterest] - informAboutUnusedKeys(collect(keys(path)), usedKeys::Vector{Symbol}, "path") - - return path -end # function checkAndSetPath! - -function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::Bool) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) != Bool - error("ERROR at checking the input for the ",dictionaryType,": The value of the key ",String(key)," is not correct. The value has to be of type Bool.") - end - else - merge!(dictionary, Dict(key => defaultValue)) - defaultValue && println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.") - end - return dictionary -end #function checkAndSetBool! - -function checkAndSetPositiveNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0 - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetPositiveNumber! - -# first method without a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # do nothing - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -# second method with a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String, default::Real) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # set a default value - merge!(dictionary, Dict(mainKey, default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",mainKey," or its value is missing. Therefore the value ",String(mainKey),"=",default," ",unit," is used." ) - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetRealNumber! - -function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::Symbol, summand1::Symbol, summand2::Symbol) - if haskey(dictionary,sum) && dictionary[sum]!=nothing - if typeof(dictionary[sum]) <: Real && dictionary[sum] >= 0.0 - difference = abs(dictionary[sum] - (dictionary[summand1]+dictionary[summand2])) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") - end - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(sum => dictionary[summand1]+dictionary[summand2])) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." ) - end - - return dictionary -end #function checkAndSetSum! - -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String, validValues::Vector{String}) - # TODO change checkAndAddString! to checkAndAddSymbol! ? - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - for validValue in validValues - if value == validValue - return dictionary - end - end - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues) - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. It has to be one of the following String values: ", validValues,". For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! -# second method of function checkAndSetString! without validValues -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String) - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - return dictionary - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.") - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! - -function checkAndSetSpeedLimit!(train::Dict) - v_limit_temp = 0.0 - v_limit_kmh_temp = 0.0 - - if haskey(train, :v_limit) && train[:v_limit]!=nothing - if typeof(train[:v_limit]) <: Real && train[:v_limit] >= 0.0 - v_limit_temp = train[:v_limit] - else - error("ERROR at checking the input for the train: The value of v_limit is no real floating point number >=0.0.") - end - end - - if haskey(train, :v_limit_kmh) && train[:v_limit_kmh]!=nothing - if typeof(train[:v_limit_kmh]) <: Real && train[:v_limit_kmh] >= 0.0 - v_limit_kmh_temp = train[:v_limit_kmh] - else - error("ERROR at checking the input for the train: The value of v_limit_kmh is no real floating point number >=0.0.") - end - else - delete!(train, :v_limit_kmh) - end - - if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0 - difference = abs(v_limit_temp - v_limit_kmh_temp/3.6) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(train, :v_limit_kmh) - println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." ) - end - elseif v_limit_temp > 0.0 - # do nothing - elseif v_limit_kmh_temp > 0.0 - merge!(train, Dict(:v_limit => v_limit_kmh_temp/3.6)) - else - # set a default value - merge!(train, Dict(:v_limit, 1000.0/3.6)) # set speed limit to 1000 km/h - println("INFO at checking the input for the train: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",train[:v_limit]," m/s is used." ) - end - - return train -end #function checkAndSetSpeedLimit! - -function checkAndSetBrakingAcceleration!(train::Dict) - if haskey(train, :a_braking) && train[:a_braking]!=nothing - if typeof(train[:a_braking]) <: Real - if train[:a_braking] > 0.0 - train[:a_braking] =-train[:a_braking] - println("INFO at checking the input for the train: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking=",train[:a_braking]," m/s^2 is used." ) - elseif train[:a_braking] == 0.0 - error("ERROR at checking the input for the train: The value for a_braking is 0.0. The braking acceleration has to be <0.0.") - end - else - error("ERROR at checking the input for the train: The value for a_braking is no real floating point number <0.0.") - end - else - # set a default value depending on the train type - if train[:type] == "freight" - a_braking = -0.225 - elseif train[:type] == "passenger" - a_braking = -0.375 - #elseif train[:type] == "passengerSuburban" - # a_braking = -0.525 - # TODO: add suburban trains to train type? - end - - merge!(train, Dict(:a_braking => a_braking)) - println("INFO at checking the input for the train: The key for a_braking is missing. Because of the train type ",train[:type]," a_braking=",a_braking," m/s^2 will be assumed and used." ) - end - - return train -end #function checkAndSetBrakingAcceleration! - -function checkAndSetRotationMassFactors!(train::Dict) - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_train, :rotationMassFactor_train, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_t, :rotationMassFactor_t, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_w, :rotationMassFactor_w, "") - if haskey(train, :ξ_train) && train[:ξ_train]!=nothing - if train[:ξ_train]>0.0 - if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing)) - # TODO: is && train[:ξ_t]>0.0 necessary here? - difference = abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train]) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") - end - end - else - error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.") - end - else - checkAndSetPositiveNumber!(train, "train", :ξ_t, "", 1.09) - - if train[:m_w]>0.0 - default_ξ_w = 1.06 - else - default_ξ_w = 0.0 - end - checkAndSetPositiveNumber!(train, "train", :ξ_w, "", default_ξ_w) - - - ξ_train=(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit) - if ξ_train <= 0.0 - error("ERROR at checking the input for the train: The train's rotations mass factor has to be higher than 0.0 kg.") - end - merge!(train, Dict(:ξ_train => ξ_train)) - end - - return train -end #function checkAndSetRotationMassFactors! - -function checkAndSetTractiveEffortVelocityPairs!(train::Dict) # pairs of velocity and tractive effort - if haskey(train,:tractiveEffortVelocityPairs) && train[:tractiveEffortVelocityPairs]!=nothing - pairs = train[:tractiveEffortVelocityPairs] - velocityMultiplier = 1.0 - - if (haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing) && (haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing) - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs, F_T_pairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - end - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - pairs = train[:F_T_pairs] - velocityMultiplier = 1.0 - - if haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." ) - end - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - velocityMultiplier = 1000/3600 - pairs=[] - for row in 1:length(train[:F_T_pairs_kmh]) - push!(pairs, [train[:F_T_pairs_kmh][row][1]*velocityMultiplier, train[:F_T_pairs_kmh][row][2]]) - end # for - - else - error("ERROR at checking the input for the train: There has to be the key tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.") - end # if - - # check if the elements of the array have the correct type - errorDetected=false - - for row in 1:length(pairs) - if typeof(pairs[row][1]) <: Real && pairs[row][1]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - if typeof(pairs[row][2]) <: Real && pairs[row][2]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - - if row>=2 && pairs[row][1] <= pairs[row-1][1] - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," (v=",pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",pairs[row-1][1]," m/s).") - end - end # for - if errorDetected - error("ERROR at checking the input for the train: Only real floating point number >=0.0 are allowed for speed and tractive effort. The speed values have to be listed from low to high.") - end - - # create tractiveEffortVelocityPairs - if pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used - newPairs=[] - push!(newPairs, [0.0, pairs[1][2]]) - println("INFO at checking the input for the train: The tractive effort for v=0.0 m/s is missing. Therefore the first given value F_T(v=",pairs[1][1]," m/s)=",pairs[1][2]," N will be used." ) - for row in 1:length(pairs) - push!(newPairs, [pairs[row][1], pairs[row][2]]) - end # for - merge!(train, Dict(:tractiveEffortVelocityPairs => newPairs)) - else - merge!(train, Dict(:tractiveEffortVelocityPairs => pairs)) - end - - if length(pairs[1])>2 - println("INFO according the train dictionary: Only the first two columns of train[:tractiveEffortVelocityPairs] are used in this tool.") - end - - return train -end #function checkAndSetTractiveEffortVelocityPairs! - -function getDefault_Δv_w(type::String) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - if type == "passenger" - # TODO if different passenger or freight trains are posiible, use: if startswith(type, "passenger"). exanples: passengerLocomotivehauled and passengerMotorCoachTrain - Δv_w=15.0/3.6 - elseif type == "freight" - Δv_w=0.0 - end # if - - return Δv_w -end #function getDefault_Δv_w! - -function checkAndSetSections!(path::Dict) - # check the section information - if haskey(path,:sections) && path[:sections]!=nothing - # TODO: check typeof(path[:sections]) == Dict - if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) - println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) - - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) - - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) - end - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts) - - if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) - end - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts_kmh) - else - error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") - section = Dict(:s_start => 0.0, - :s_end => 15.0, - :v_limit => 1000.0/3.6, - :f_Rp => 0.0) - merge!(path, Dict(:sections => [section])) - return path - end - - sections = path[:sections] - - checkedSections = [] - increasing = false - decreasing = false - - #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? - - # check values for section==1 - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m", 0.0) # first point of the section (in m) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m", 0.0) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[1], "path[:sections][1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - - push!(checkedSections, sections[1]) - - if sections[1][:s_start] < sections[1][:s_end] - increasing = true - elseif sections[1][:s_start] > sections[1][:s_end] - decreasing = true - else - pop!(checkedSections) - println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end - - - for sectionNr in 2:length(sections) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) - # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values - #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real - # defaultEnd = sections[sectionNr+1][:s_start] - #end - defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - - push!(checkedSections, sections[sectionNr]) - - # compare the section's start and end position - if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] - increasing = true - elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] - decreasing = true - else - pop!(checkedSections) - println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end - if increasing && decreasing - error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") - end - - - if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] - error("ERROR at checking the input for the path[:sections]: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") - # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? - end - end #for - - return path -end #function checkAndSetSections! - -function createSections!(path::Dict, key::Symbol) - # read the section starting positions and corresponding information - if key == :sectionStarts - sectionStartsArray = path[:sectionStarts] - conversionFactor = 1.0 # conversion factor between the units m/s and m/s - - if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) - end - elseif key == :sectionStarts_kmh - sectionStartsArray = path[:sectionStarts_kmh] - conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s - else - error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") - end # if - - # check if the array is correct and if elements of the array have the correct type and valid values - errorDetected = false - if length(sectionStartsArray)<2 - error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") - end - - for row in 1:length(sectionStartsArray) - if length(sectionStartsArray[row])>=3 - if length(sectionStartsArray[row])>3 - println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") - end - else - error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") - end - - if !(typeof(sectionStartsArray[row][1]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") - end - if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) - errorDetected=true - println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") - end - if !(typeof(sectionStartsArray[row][3]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") - end - end # for - if errorDetected - error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") - end - - - sections = [] - for row in 2:length(sectionStartsArray) - s_start = sectionStartsArray[row-1][1] # first point of the section (in m) - s_end = sectionStartsArray[row][1] # first point of the next section (in m) - v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) - f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) - - section = Dict(:s_start => s_start, - :s_end => s_end, - :v_limit => v_limit, - :f_Rp => f_Rp) - push!(sections, section) - end # for - # s_start in first entry defines the path's beginning - # s_end in last entry defines the path's ending - - merge!(path, Dict(:sections => sections)) - return path -end #function createSections! - -function checkAndSetPOIs!(path::Dict) - # read the section starting positions and corresponding information - if haskey(path, :pointsOfInterest) - if path[:pointsOfInterest] != nothing - pointsOfInterest = path[:pointsOfInterest] - - sortingNeeded = false - errorDetected = false - for element in 1:length(pointsOfInterest) - if typeof(pointsOfInterest[element]) <: Real - if element > 1 - if pointsOfInterest[element] < pointsOfInterest[element-1] - sortingNeeded = true - println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") - end - end - else - errorDetected = true - println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") - end - end # for - - if errorDetected - error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") - end - if sortingNeeded == true - sort!(pointsOfInterest) - end - - copiedPOIs = [] - for element in 1:length(pointsOfInterest) - if element == 1 - push!(copiedPOIs, pointsOfInterest[element]) - elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] - push!(copiedPOIs, pointsOfInterest[element]) - end - end # for - path[:pointsOfInterest] = copiedPOIs - - else - println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") - delete!(path, :pointsOfInterest) - end - end - - return path -end #function checkAndSetPOIs! - -#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool -function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool - unusedKeys = [] - # find unused keys in allKeys - for key in allKeys - used = false - for usedKey in usedKeys - if key == usedKey - used = true - break - end - end - if !used - push!(unusedKeys, key) - end - end - - if length(unusedKeys)>0 - println("INFO at checking the input for the ",dictionaryType,": The following Keywords are not used in this tool:") - for key in unusedKeys - println(" - ",key) - end - end -end #function informAboutUnusedKeys From 9b00bb8b1283e17583149d837b5bc65174574804 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Sat, 30 Apr 2022 12:37:22 +0200 Subject: [PATCH 05/18] improved description fro Julia environment --- CONTRIBUTING.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa259f2..b10c3e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,11 +7,23 @@ Please note we have a code of conduct, please follow it in all your interactions # Julia Development Environment -``` +Link your local git repository to Julia: +```console $ ln -s ~/path/to/TrainRun.jl ~/.julia/dev/TrainRun ``` -and use `Revise.jl` +Have a look here how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev +You might want to use `Revise.jl` as well: +```julia +Pkg.add("Revise") +``` +and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file) + +```julia +(@v1.x) pkg> develop TrainRun +(@v1.x) pkg> activate TrainRun +(TrainRun) pkg> +``` # Pull Request Process From 00eda496f5535e4007055cef406b243b2ddf2857 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Wed, 4 May 2022 16:34:17 +0200 Subject: [PATCH 06/18] new type Path as a struct, working test sets, structure rework --- .github/workflows/CI.yml | 5 + CHANGELOG.md | 9 +- CONTRIBUTING.md | 61 +- Project.toml | 9 +- README.md | 25 +- docs/examples/ExtendedWorkingExample.jl | 2 +- docs/examples/MinimalWorkingExample.jl | 8 +- src/TrainRun.jl | 48 +- src/behavior.jl | 164 ++--- src/calc.jl | 37 +- src/characteristics.jl | 75 +-- src/output.jl | 28 +- src/types.jl | 654 +++++++++++++------ test/data/paths/const.yaml | 20 +- test/data/paths/realworld.yaml | 706 +++++++++++---------- test/data/paths/slope.yaml | 38 +- test/data/paths/speed.yaml | 36 +- test/data/settings/detail.yaml | 9 - test/data/settings/everything.yaml | 4 + test/data/settings/points_of_interest.yaml | 4 + test/runtests.jl | 120 ++-- 21 files changed, 1173 insertions(+), 889 deletions(-) delete mode 100644 test/data/settings/detail.yaml create mode 100644 test/data/settings/everything.yaml create mode 100644 test/data/settings/points_of_interest.yaml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 68202e0..78438f5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,12 +18,17 @@ jobs: fail-fast: false matrix: version: + - '1.6' - '1.7' - 'nightly' os: - ubuntu-latest + - macOS-latest + - windows-latest arch: + - x86 - x64 + - aarch64 steps: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 37241e6..32fea41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,19 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. ### Changed * replaced settings::Dict with type Settings as struct +* replaced path::Dict with type Path 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 tilte of include files from upper case to lower case +* moved trainrun function from calc.jl to TrainRun.jl +* moved createDataPoint() from behavior.jl to types.jl +* moved createBehaviorSection() from behavior.jl to types.jl +* moved createMovingSection() from characteristics.jl to types.jl +* moved createCharacteristicSection() from characteristics.jl to types.jl +* changed title of include files from upper case to lower case * changed seperation of submodules into a single module with file include +* updated test files to railtoolkit/schema (2022.04) ### Removed * dependency Plots diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b10c3e5..3bcb403 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,28 +5,77 @@ 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 +## Julia Development Environment Link your local git repository to Julia: ```console $ ln -s ~/path/to/TrainRun.jl ~/.julia/dev/TrainRun ``` -Have a look here how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev +Have a look how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev You might want to use `Revise.jl` as well: ```julia Pkg.add("Revise") ``` -and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file) +and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file). +You can overide the standard TrainRun package with the local development branch (see linking above) with: ```julia +julia> # use the ] key (@v1.x) pkg> develop TrainRun +(@v1.x) pkg> # use backspace +julia> using TrainRun # local development branch will be loaded +``` + +If you want to add a dependency use: +```julia +julia> # use the ] key (@v1.x) pkg> activate TrainRun (TrainRun) pkg> ``` +## TrainRun files -# Pull Request Process +TODO! - * add your changes to the CHANGELOG.md under [Unreleased] - * TODO! \ No newline at end of file +## Reporting Issues + +* It's always good to start with a quick search for an existing issue to post on, + or related issues for context, before opening a new issue +* Including minimal examples is greatly appreciated +* If it's a bug, or unexpected behaviour, reproducing on the latest development version + (`Pkg.add(name="TrainRun", rev="master")`) is a good gut check and can streamline the process, + along with including the first two lines of output from `versioninfo()` + +## Style Guidelines + +TODO + +## Git Recommendations For Pull Requests + +* Avoid working from the `master` branch of your fork, creating a new branch will make it + easier if TrainRun.jl `master` branch changes and you need to update your pull request; +* All PRs and issues should be opened against the `master` branch not against the current release; +* Run tests of your code before sending any commit to GitHub. Only push changes when + the tests of the change are passing locally. In particular note that it is not a problem + if you send several commits in one push command to GitHub as CI will be run only once then; +* If any conflicts arise due to changes in TrainRun.jl `master` branch, prefer updating your pull + request branch with `git rebase` (rather than `git merge`), since the latter will introduce a merge + commit that might confuse GitHub when displaying the diff of your PR, which makes your changes more + difficult to review. Alternatively use conflict resolution tool available at GitHub; +* Please try to use descriptive commit messages to simplify the review process; +* Using `git add -p` or `git add -i` can be useful to avoid accidently committing unrelated changes; +* Maintainers get notified of all changes made on GitHub. However, what is useful is writing a short + message after a sequence of changes is made summarizing what has changed and that the PR is ready + for a review; +* When linking to specific lines of code in discussion of an issue or pull request, hit the `y` key + while viewing code on GitHub to reload the page with a URL that includes the specific commit that + you're viewing. That way any lines of code that you refer to will still be correct in the future, even + if additional commits are pushed to the branch you are reviewing; +* Please make sure you follow the code formatting guidelines when submitting a PR; + Also preferably do not modify parts of code that you are not editing as this makes + reviewing the PR harder (it is better to open a separate maintenance PR + if e.g. code layout can be improved); +* If a PR is not finished yet and should not be reviewed yet then it should be opened as DRAFT + (in this way maintainers will know that they can ignore such PR until it is made non-draft or the author + asks for a review). diff --git a/Project.toml b/Project.toml index a989380..a5fefaf 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "TrainRun" uuid = "e4541106-d44c-4e00-b50b-ecdf479fcf92" -authors = ["Max Kannenberg, Martin Scheidt, and contributors"] +authors = ["Max Kannenberg", "Martin Scheidt", "contributors"] version = "0.8.0" [deps] @@ -8,10 +8,15 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" +UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" [compat] -julia = "1.7" +CSV = "^0" +DataFrames = "^1" +JSONSchema = "^1" +YAML = "^0" +julia = "^1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/README.md b/README.md index 75e0a3a..a0aea6e 100644 --- a/README.md +++ b/README.md @@ -14,28 +14,25 @@ TrainRun.jl is a step towards open science and open data in railway engineering. # Installation The required julia packages are - - YAML.jl - - Dates.jl - - DataFrames.jl - - CSV.jl - - Plots.jl - -Review the settings.yaml file for your appropriate settings. + - YAML.jl + - JSONSchema.jl + - Dates.jl + - DataFrames.jl + - CSV.jl ------------ # Minimal working example ```julia -include("../src/TrainRun.jl") -using .TrainRun +import TrainRun -train_directory = "data/trains/train_freight_V90withOreConsist.yaml" -running_path_directory = "data/paths/path_1_10km_nConst_vConst.yaml" -settings_directory = "data/settings.yaml" -(train, running_path, settings) = importYamlFiles(train_directory, running_path_directory, setting_directory) +train = Train("test/data/trains/freight.yaml") +path = Path("test/data/paths/const.yaml") -train_run = trainRun(train, running_path, settings) +runtime = trainrun(train, path) + +println("The train needs $runtime seconds for the running path.") ``` ------------ diff --git a/docs/examples/ExtendedWorkingExample.jl b/docs/examples/ExtendedWorkingExample.jl index e3c8c58..f6a4123 100644 --- a/docs/examples/ExtendedWorkingExample.jl +++ b/docs/examples/ExtendedWorkingExample.jl @@ -22,7 +22,7 @@ for path in paths for train in trains # println("train: ", train[:name]) for settings in settings - resultsDict = trainRun(train, path, settings) + resultsDict = trainrun(train, path, settings) if haskey(settings, :outputFormat) && settings[:outputFormat] == "CSV" exportToCsv(resultsDict, settings) sleep(2) diff --git a/docs/examples/MinimalWorkingExample.jl b/docs/examples/MinimalWorkingExample.jl index 581421e..d760abc 100644 --- a/docs/examples/MinimalWorkingExample.jl +++ b/docs/examples/MinimalWorkingExample.jl @@ -1,10 +1,10 @@ #!/usr/bin/env julia -import TrainRun +using TrainRun -train = Train("data/trains/train_freight_V90withOreConsist.yaml") -path = Path("data/paths/path_1_10km_nConst_vConst.yaml") +train = Train("test/data/trains/freight.yaml") +path = Path("test/data/paths/const.yaml") -runtime = trainRun(train, path) +runtime = trainrun(train, path) println("The train needs $runtime seconds for the running path.") diff --git a/src/TrainRun.jl b/src/TrainRun.jl index 2f614fe..aacaa89 100644 --- a/src/TrainRun.jl +++ b/src/TrainRun.jl @@ -8,8 +8,14 @@ __precompile__(true) module TrainRun +## loading standard library packages +using UUIDs, Dates ## loading external packages -using YAML, JSONSchema, CSV, DataFrames, Dates +using YAML, JSONSchema, CSV, DataFrames + +export +## Interface +trainrun, Path, Settings, exportToCsv ## include package files include("types.jl") @@ -21,10 +27,42 @@ include("import.jl") include("export.jl") include("calc.jl") -export -## Interface -trainRun, Settings, exportToCsv +## main function +""" + trainrun(train::Dict, path::Path, settings::Settings) -## main functions +Calculate the running time of a `train` on a `path`. +The `settings` provides the choice of models for the calculation. +`settings` can be omitted. If so, a default is used. +The running time will be return in seconds. + +# Examples +```julia-repl +julia> trainrun(train, path) +xxx.xx # in seconds +``` +""" +function trainrun(trainInput::Dict, path::Path, settings=Settings()::Settings) + # copy Input data for not changing them + # TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && isempty(path.poi) + train = copy(trainInput) + + # check the input data + train = checkAndSetTrain!(train) + settings.outputDetail == :everything && println("The input has been checked.") + + # prepare the input data + movingSection = determineCharacteristics(path, train, settings) + settings.outputDetail == :everything && println("The moving section has been prepared.") + + # calculate the train run for oparation mode "minimum running time" + (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) + + return output +end # function trainrun end # module TrainRun diff --git a/src/behavior.jl b/src/behavior.jl index d1815d2..ef03b07 100644 --- a/src/behavior.jl +++ b/src/behavior.jl @@ -176,9 +176,12 @@ function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s:: return currentSpeedLimit end #function getCurrentSpeedLimit -function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real) +""" +? +""" +function getNextPointOfInterest(pointsOfInterest::Vector{Tuple}, s::Real) for s_POI in pointsOfInterest - if s_POI > s + if s_POI[1] > s return s_POI end end @@ -327,7 +330,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla while !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached && !previousSpeedLimitReached 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 + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] 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 @@ -335,7 +338,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla end while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached - # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit + # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) @@ -360,7 +363,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla previousSpeedLimitReached = currentSpeedLimit[:v] < CS[:v_limit] && (drivingCourse[end][:v] > currentSpeedLimit[:v] || (drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end])) targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] #targetSpeedReached = speedLimitReached - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] end #while @@ -380,10 +383,10 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla 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 - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest[1]) # for testing if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s] else currentStepSize = settings.stepSize / 10.0^cycle end @@ -420,16 +423,16 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing break - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing - if nextPointOfInterest == CS[:s_exit] + elseif drivingCourse[end][:s] == nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest[1]) # for testing + if nextPointOfInterest[1] == CS[:s_exit] endOfCSReached = true end break else println("v=",drivingCourse[end][:v]," v_peak= ", CS[:v_peak] , " v_cLimit=", currentSpeedLimit[:v]) - println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest) + println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest[1]) 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 nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing + drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] elseif drivingCourse[end][:F_T] <= drivingCourse[end][:F_R] @@ -590,12 +593,12 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: # 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 - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + nextPointOfInterest[1] = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] 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] + # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] # the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS #TODO: maybe just consider former CS with different path resistance? # tractive effort (in N): @@ -629,7 +632,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: #end # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] @@ -647,9 +650,9 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: elseif trainIsBrakingDownhill && !resistingForceNegative currentStepSize = settings.stepSize / 10.0^cycle - elseif drivingCourse[end][:s] > nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s] else currentStepSize = settings.stepSize / 10.0^cycle end @@ -667,7 +670,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] break - elseif drivingCourse[end][:s] == nextPointOfInterest + elseif drivingCourse[end][:s] == nextPointOfInterest[1] break elseif !trainInPreviousCS @@ -689,8 +692,8 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: resistingForceNegative = drivingCourse[end][:F_R] < 0.0 else # if the level of approximation is reached - if drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + if drivingCourse[end][:s] > nextPointOfInterest[1] + drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] elseif drivingCourse[end][:s] > BS[:s_entry]+s_cruising if BS[:type] != "clearing" @@ -723,7 +726,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: while !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used # 03/09 old: while drivingCourse[end][:s] < BS[:s_entry]+s_cruising && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])) + nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])[1]) # tractive effort (in N): #03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) @@ -739,7 +742,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: # calculate the remaining cruising way #s_cruisingRemaining=BS[:s_entry] + s_cruising-drivingCourse[end][:s] - s_cruisingRemaining = min(nextPointOfInterest -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s]) + s_cruisingRemaining = min(nextPointOfInterest[1] -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s]) # create the next data point push!(drivingCourse, moveAStep(drivingCourse[end], :distance, s_cruisingRemaining, CS[:id])) @@ -818,12 +821,12 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached 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 + nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])[1] + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] 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 + # 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end # acceleration (in m/s^2): drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) @@ -839,7 +842,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) end brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] targetSpeedReached = drivingCourse[end][:v] <= 0.0 tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] endOfCSReached = drivingCourse[end][:s] == CS[:s_exit] @@ -867,10 +870,10 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag 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 - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest[1]) # for testing if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s] else currentStepSize = settings.stepSize / 10.0^cycle end @@ -879,8 +882,8 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag 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 break - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing + elseif drivingCourse[end][:s] == nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest[1]) # for testing break elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] @@ -924,9 +927,9 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag tractionDeficit = true endOfCSReached = false - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing + drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] elseif drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] @@ -936,7 +939,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag else testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: else with v=", drivingCourse[end][:v]," > 0.0 and F_T=", drivingCourse[end][:F_T]," <= F_R=", drivingCourse[end][:F_R]) # for testing #println(" and s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," <= s_exit=",CS[:s_exit]) # for testing - #println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest=",nextPointOfInterest) # for testing + #println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing # if drivingCourse[end][:s] + s_braking == CS[:s_exit] # brakingStartReached = true @@ -998,12 +1001,12 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: while !targetSpeedReached && !endOfCSReached && !brakingStartReached 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 + nextPointOfInterest[1] = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] 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 + # 03/09 old : while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:v] <= CS[:v_peak] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] # traction effort and resisting forces (in N): calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) @@ -1018,7 +1021,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: # conditions for the next while cycle s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] end # while @@ -1030,11 +1033,11 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: 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 - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s] else currentStepSize = settings.stepSize / 10.0^cycle end @@ -1061,8 +1064,8 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," == v_exit=", CS[:v_exit]) # for testing break - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing + elseif drivingCourse[end][:s] == nextPointOfInterest[1] + testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing break else @@ -1105,8 +1108,8 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: pointOfInterestReached = false # targetSpeedReached = true - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] else # do nothing for example for drivingCourse[end][:s] + s_braking == CS[:s_exit] @@ -1156,11 +1159,11 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D while !targetSpeedReached && !endOfCSReached 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 + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] 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 + # 03/09 old: while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest[1] # traction effort and resisting forces (in N): calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) @@ -1184,7 +1187,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D #println(drivingCourse[end][:i],". s=",drivingCourse[end][:s]," s_exit=", CS[:s_exit]," v_exit=", CS[:v_exit]," v=",drivingCourse[end][:v]) # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest + pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] end # while @@ -1193,14 +1196,14 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D # TODO: is there a better way than rounding like in the following? if cycle < settings.approxLevel+1 if drivingCourse[end][:v] < CS[:v_exit] - if settings.stepVariable == velocity + if settings.stepVariable == :velocity currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] else currentStepSize = settings.stepSize / 10.0^cycle end - elseif drivingCourse[end][:s] > nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] + currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s] else currentStepSize = settings.stepSize / 10.0^cycle end @@ -1217,7 +1220,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D targetSpeedReached = true # println(" with a=", drivingCourse[end-1][:a]) # for testing break - elseif drivingCourse[end][:s] == nextPointOfInterest + elseif drivingCourse[end][:s] == nextPointOfInterest[1] break end @@ -1243,8 +1246,8 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D # recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) drivingCourse[end][:s] = CS[:s_exit] break - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest + elseif drivingCourse[end][:s] > nextPointOfInterest[1] + drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] break elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] @@ -1266,7 +1269,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D targetSpeedReached = true break else - # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest + # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest[1] end end end #for @@ -1332,47 +1335,6 @@ function mergeBehaviorSection!(BSs::Dict, BS::Dict) return BSs end #function mergeBehaviorSection! -function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) - BS= Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill - :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" - :length => 0.0, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => 0.0, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_entry => v_entry, # entry speed (in m/s) - :v_exit => 0.0, # exit speed (in m/s) - :dataPoints => [startingPoint]) # list of identifiers of the containing data points starting with the initial point - return BS -end #function createBehaviorSection - -""" -a data point is the smallest element of the driving course. One step of the step approach is between two data points -""" -function createDataPoint() - dataPoint = Dict(:i => 0, # identifier and counter variable of the dricing course - :behavior => "", # type of behavior section the data point is part of ("breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") - # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter - :s => 0.0, # position (in m) - :Δs => 0.0, # step size (in m) - :t => 0.0, # point in time (in s) - :Δt => 0.0, # step size (in s) - :v => 0.0, # velocity (in m/s) - :Δv => 0.0, # step size (in m/s) - :a => 0.0, # acceleration (in m/s^2) - :W => 0.0, # mechanical work (in Ws) - :ΔW => 0.0, # mechanical work in this step (in Ws) - :E => 0.0, # energy consumption (in Ws) - :ΔE => 0.0, # energy consumption in this step (in Ws) - :F_T => 0.0, # tractive effort (in N) - :F_R => 0.0, # resisting force (in N) - :R_path => 0.0, # path resistance (in N) - :R_train => 0.0, # train resistance (in N) - :R_traction => 0.0, # traction unit resistance (in N) - :R_wagons => 0.0) # set of wagons resistance (in N) - return dataPoint -end #function createDataPoint - function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) currentPoint = drivingCourse[end] previousPoint = drivingCourse[end-1] diff --git a/src/calc.jl b/src/calc.jl index 3ba2662..879f3d2 100644 --- a/src/calc.jl +++ b/src/calc.jl @@ -5,42 +5,7 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -# 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::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) -todo !!! -``` -""" -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.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) - train = copy(trainInput) - path = copy(pathInput) - - # check the input data - (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.outputDetail == :everything && println("The moving section has been prepared.") - - # calculate the train run for oparation mode "minimum running time" - (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) - - return output -end # function trainRun +# Calculate the running time of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. # calculate a train run focussing on using the minimum possible running time function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) diff --git a/src/characteristics.jl b/src/characteristics.jl index 4dd83c2..f0e3baf 100644 --- a/src/characteristics.jl +++ b/src/characteristics.jl @@ -6,8 +6,8 @@ # __license__ = "ISC" ## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior -function determineCharacteristics(path::Dict, train::Dict, settings::Settings) - movingSection = createMovingSection(path, train[:v_limit]) +function determineCharacteristics(path::Path, train::Dict, settings::Settings) + movingSection = createMovingSection(path, train[:v_limit], train[:length]) movingSection = secureBrakingBehavior!(movingSection, train[:a_braking]) movingSection = secureAcceleratingBehavior!(movingSection, settings, train) #movingSection = secureCruisingBehavior!(movingSection, settings, train) @@ -15,77 +15,6 @@ function determineCharacteristics(path::Dict, train::Dict, settings::Settings) return movingSection end #function determineCharacteristics -## create a moving section containing characteristic sections -function createMovingSection(path::Dict, v_trainLimit::Real) - # this function creates and returns a moving section dependent on the paths attributes - - s_entry = path[:sections][1][:s_start] # first position (in m) - s_exit = path[:sections][end][:s_end] # last position (in m) - pathLength = s_exit - s_entry # total length (in m) - - CSs=Vector{Dict}() - s_csStart=s_entry - csId=1 - for row in 2:length(path[:sections]) - previousSection = path[:sections][row-1] - currentSection = path[:sections][row] - speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) - pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] - if speedLimitIsDifferent || pathResistanceIsDifferent - # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] - push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), path)) - s_csStart = currentSection[:s_start] - csId = csId+1 - end #if - end #for - push!(CSs, createCharacteristicSection(csId, s_csStart, path[:sections][end], min(path[:sections][end][:v_limit], v_trainLimit), path)) - - movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore - :length => pathLength, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => s_exit, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :characteristicSections => CSs) # list of containing characteristic sections - - return movingSection -end #function createMovingSection - - -## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. -function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, path::Dict) - # Create and return a characteristic section dependent on the paths attributes - characteristicSection= Dict(:id => id, # identifier - :s_entry => s_entry, # first position (in m) - :s_exit => section[:s_end], # last position (in m) - :length => section[:s_end] -s_entry, # total length (in m) - :r_path => section[:f_Rp], # path resistance (in ‰) - :behaviorSections => Dict(), # list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => v_limit, # speed limit (in m/s) - # initializing :v_entry, :v_peak and :v_exit with :v_limit - :v_peak => v_limit, # maximum reachable speed (in m/s) - :v_entry => v_limit, # maximum entry speed (in m/s) - :v_exit => v_limit) # maximum exit speed (in m/s) - - # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated - s_exit = characteristicSection[:s_exit] - pointsOfInterest = Vector{Real}() - if haskey(path, :pointsOfInterest) - for POI in path[:pointsOfInterest] - if s_entry < POI && POI < s_exit - push!(pointsOfInterest, POI) - end - end - end - push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with - - merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) - - return characteristicSection -end #function createCharacteristicSection - ## define the intersection velocities between the characterisitc sections to secure braking behavior function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) # this function limits the entry and exit velocity of the characteristic sections to secure that the train stops at the moving sections end diff --git a/src/output.jl b/src/output.jl index a21c12e..ae5d25b 100644 --- a/src/output.jl +++ b/src/output.jl @@ -5,18 +5,22 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -function createOutput(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) +function createOutput(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) if settings.outputDetail == :running_time output = movingSection[:t] # TODO: or use drivingCourse[end][:t] elseif settings.outputDetail == :points_of_interest # add points of interest - if haskey(path, :pointsOfInterest) - output = Vector{Dict}() + if !isempty(path.poi) + + # for elem in 1:length(driving_course) + # end + + output = Dict[] POI = 1 i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + while POI <= length(path.poi) && i <= drivingCourse[end][:i] + if path.poi[POI][:station] == drivingCourse[i][:s] push!(output, drivingCourse[i]) POI = POI+1 end @@ -42,12 +46,12 @@ function createOutput(train::Dict, settings::Settings, path::Dict, movingSection end # add points of interest - if haskey(path, :pointsOfInterest) + if !isempty(path.poi) pointsOfInterest = Vector{Dict}() POI = 1 i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + while POI <= length(path.poi) && i <= drivingCourse[end][:i] + if path.poi[POI] == drivingCourse[i][:s] push!(pointsOfInterest, drivingCourse[i]) POI = POI+1 end @@ -67,7 +71,7 @@ function createOutput(train::Dict, settings::Settings, path::Dict, movingSection end #= -function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) +function createOutputDict(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) outputDict = Dict{Symbol,Any}() merge!(outputDict, Dict(:train => train, :path => path, :settings => settings)) @@ -82,12 +86,12 @@ function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSec end # add points of interest - if haskey(path, :pointsOfInterest) + if !isempty(path.poi) pointsOfInterest = Vector{Dict}() POI = 1 i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] + while POI <= length(path.poi) && i <= drivingCourse[end][:i] + if path.poi[POI] == drivingCourse[i][:s] push!(pointsOfInterest, drivingCourse[i]) POI = POI+1 end diff --git a/src/types.jl b/src/types.jl index 3419b44..8f9de9b 100644 --- a/src/types.jl +++ b/src/types.jl @@ -41,7 +41,9 @@ struct Settings outputFormat = :julia_dict outputDir = "." + ## load from file if file != "DEFAULT" + ## JSON schema for YAML-file validation schema = Schema("""{ "properties": { @@ -83,7 +85,7 @@ struct Settings try validate(schema, settings) catch err - println("Could not load settings file $file.\n Format is not recognized - using default as fall back.") + println("Could not load settings file '$file'.\n Format is not recognized - using default as fall back.") settings = Dict() end @@ -104,18 +106,283 @@ struct Settings 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::Settings) - checkAndSetTrain!(train) - checkAndSetPath!(path) + Path(file, type = :YAML) - 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.")) +Path is a datastruture for calculation context. +The function Path() will create a running path for the train. + +# Example +```jldoctest +julia> my_path = Path("file.yaml") # will generate a path from a YAML file. +Path(variables) +``` +""" +struct Path + + name::String # a name or description of the path + id::String # a short string as identifier + uuid::UUID # a unique identifier + poi::Vector # a vector of triples with points along the path + sections::Vector # a vector of the characteristic sections + + ## constructor + function Path(file, type = :YAML) + + ## default values + name = "" + id = "" + uuid = UUIDs.uuid4() + poi = [] + sections = [] + + ## process flags + POI_PRESENT = false + + ## load from file + if type == :YAML + + path = YAML.load(open(file))["path"] + + ## JSON schema for YAML-file validation + railtoolkit_schema = Schema("""{ + "required": [ "name", "id", "characteristic_sections" ], + "properties": { + "characteristic_sections": { + "description": "", + "type": "array", + "minItems": 2, + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { "type": "number" }, + { "type": "number" }, + { "type": "number" } + ] + } + }, + "id": { + "description": "Identifier of the path", + "type": "string" + }, + "name": { + "description": "Name of the path", + "type": "string" + }, + "points_of_interest": { + "description": "", + "type": "array", + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { "type": "number" }, + { "type": "string" }, + { "enum": [ "front", "rear" ] } + ] + } + }, + "UUID": { + "description": "The unique identifier for a path", + "type": "string", + "format": "uuid" + } + } + }""") + + try + validate(railtoolkit_schema, path) + catch err + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema (2022.04)") + end + + ## set the variables if they exist in "settings" + # required + name = path["name"] + id = path["id"] + tmp_sec = path["characteristic_sections"] + # optional + haskey(path, "UUID") ? uuid = parse(UUID, path["UUID"] ) : nothing + haskey(path, "points_of_interest") ? POI_PRESENT = true : nothing + haskey(path, "points_of_interest") ? tmp_points = path["points_of_interest"] : nothing + + else + error("Unknown file type '$type'") + end #if type + + ## process characteristic sections + sort!(tmp_sec, by = x -> x[1]) + for row in 2:length(tmp_sec) + s_start = tmp_sec[row-1][1] # first point of the section (in m) + s_end = tmp_sec[row][1] # first point of the next section (in m) + v_limit = tmp_sec[row-1][2]/3.6 # paths speed limt (in m/s) + f_Rp = tmp_sec[row-1][3] # specific path resistance of the section (in ‰) + + section = Dict(:s_start => s_start, + :s_end => s_end, + :v_limit => v_limit, + :f_Rp => f_Rp) + push!(sections, section) + end #for row + # s_start in first entry defines the path's beginning + # s_end in last entry defines the path's ending + + ## process points of interest + if POI_PRESENT + sort!(tmp_points, by = x -> x[1]) + for elem in tmp_points + station = elem[1] # first point of the section (in m) + label = elem[2] # paths speed limt (in m/s) + measure = elem[3] # specific path resistance of the section (in ‰) + + point = Dict(:station => station, + :label => label, + :measure => measure) + push!(poi, point) + end #for elem + end #if !isempty(points) + + new(name, id, uuid, poi, sections) + + end #function Path + +end #struct Path + +""" +a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points +""" +function createDataPoint() + dataPoint = Dict( + :i => 0, # identifier and counter variable of the driving course + :behavior => "", # type of behavior section the data point is part of - see createBehaviorSection() + # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter + :s => 0.0, # position (in m) + :Δs => 0.0, # step size (in m) + :t => 0.0, # point in time (in s) + :Δt => 0.0, # step size (in s) + :v => 0.0, # velocity (in m/s) + :Δv => 0.0, # step size (in m/s) + :a => 0.0, # acceleration (in m/s^2) + :W => 0.0, # mechanical work (in Ws) + :ΔW => 0.0, # mechanical work in this step (in Ws) + :E => 0.0, # energy consumption (in Ws) + :ΔE => 0.0, # energy consumption in this step (in Ws) + :F_T => 0.0, # tractive effort (in N) + :F_R => 0.0, # resisting force (in N) + :R_path => 0.0, # path resistance (in N) + :R_train => 0.0, # train resistance (in N) + :R_traction => 0.0, # traction unit resistance (in N) + :R_wagons => 0.0, # set of wagons resistance (in N) + :label => "" # a label for importend points + ) + return dataPoint +end #function createDataPoint + +""" +BehaviorSection() TODO! +""" +function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) + BS= Dict( + :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" + :length => 0.0, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => 0.0, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_entry => v_entry, # entry speed (in m/s) + :v_exit => 0.0, # exit speed (in m/s) + :dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point + ) + return BS +end #function createBehaviorSection + +## create a moving section containing characteristic sections +function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) + # this function creates and returns a moving section dependent on the paths attributes + + s_entry = path.sections[1][:s_start] # first position (in m) + s_exit = path.sections[end][:s_end] # last position (in m) + pathLength = s_exit - s_entry # total length (in m) + + CSs=Vector{Dict}() + s_csStart=s_entry + csId=1 + for row in 2:length(path.sections) + previousSection = path.sections[row-1] + currentSection = path.sections[row] + speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) + pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] + if speedLimitIsDifferent || pathResistanceIsDifferent + # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] + push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path)) + s_csStart = currentSection[:s_start] + csId = csId+1 + end #if + end #for + push!(CSs, createCharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path)) + + movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore + :length => pathLength, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => s_exit, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :characteristicSections => CSs) # list of containing characteristic sections + + return movingSection +end #function createMovingSection + + +## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. +function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path) + # Create and return a characteristic section dependent on the paths attributes + characteristicSection= Dict(:id => id, # identifier + :s_entry => s_entry, # first position (in m) + :s_exit => section[:s_end], # last position (in m) + :length => section[:s_end] -s_entry, # total length (in m) + :r_path => section[:f_Rp], # path resistance (in ‰) + :behaviorSections => Dict(), # list of containing behavior sections + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_limit => v_limit, # speed limit (in m/s) + # initializing :v_entry, :v_peak and :v_exit with :v_limit + :v_peak => v_limit, # maximum reachable speed (in m/s) + :v_entry => v_limit, # maximum entry speed (in m/s) + :v_exit => v_limit) # maximum exit speed (in m/s) + + # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated + s_exit = characteristicSection[:s_exit] + + ##TODO: use a tuple with naming + pointsOfInterest = Tuple[] + # pointsOfInterest = Real[] + if !isempty(path.poi) + for POI in path.poi + s_poi = POI[:station] + if POI[:measure] == "rear" + s_poi -= s_trainLength + end + if s_entry < s_poi && s_poi < s_exit + push!(pointsOfInterest, (s_poi, POI[:label]) ) + # push!(pointsOfInterest, s_poi ) + end + end end - return (train, path) -end #function checkAndSetInput! + push!(pointsOfInterest, (s_exit,"")) # s_exit has to be the last POI so that there will always be a POI to campare the current position with + # push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with + + merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) + + return characteristicSection +end #function createCharacteristicSection """ Read the train information from a YAML file, save it in a train Dict and return it. @@ -174,7 +441,7 @@ function checkAndSetTrain!(train::Dict) return train end #function checkAndSetTrain! -function checkAndSetPath!(path::Dict) +function checkAndSetPath!(path::Path) # check path information from input dictionary checkAndSetString!(path, "path", :name, "") @@ -561,217 +828,220 @@ function getDefault_Δv_w(type::String) # coefficient for velocitiy differen return Δv_w end #function getDefault_Δv_w! -function checkAndSetSections!(path::Dict) - # check the section information - if haskey(path,:sections) && path[:sections]!=nothing - # TODO: check typeof(path[:sections]) == Dict - if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) - println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) +# function checkAndSetSections!(path::Path) +# # check the section information +# if haskey(path,:sections) && path.sections!=nothing +# # TODO: check typeof(path.sections) == Dict +# if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) +# println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) +# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing +# println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) - end - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts) +# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing +# println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) +# end +# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing +# # TODO: check typeof(path.sections) == Array +# createSections!(path, :sectionStarts) - if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) - end - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts_kmh) - else - error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") - section = Dict(:s_start => 0.0, - :s_end => 15.0, - :v_limit => 1000.0/3.6, - :f_Rp => 0.0) - merge!(path, Dict(:sections => [section])) - return path - end +# if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing +# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) +# end +# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing +# # TODO: check typeof(path.sections) == Array +# createSections!(path, :sectionStarts_kmh) +# else +# error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") +# section = Dict(:s_start => 0.0, +# :s_end => 15.0, +# :v_limit => 1000.0/3.6, +# :f_Rp => 0.0) +# merge!(path, Dict(:sections => [section])) +# return path +# end - sections = path[:sections] +# sections = path.sections - checkedSections = [] - increasing = false - decreasing = false +# checkedSections = [] +# increasing = false +# decreasing = false - #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? +# #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? - # check values for section==1 - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m", 0.0) # first point of the section (in m) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m", 0.0) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[1], "path[:sections][1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) +# # check values for section==1 +# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_start, "m", 0.0) # first point of the section (in m) +# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_end, "m", 0.0) # first point of the next section (in m) +# checkAndSetPositiveNumber!(sections[1], "path.sections[1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) +# checkAndSetRealNumber!(sections[1], "path.sections[1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - push!(checkedSections, sections[1]) +# push!(checkedSections, sections[1]) - if sections[1][:s_start] < sections[1][:s_end] - increasing = true - elseif sections[1][:s_start] > sections[1][:s_end] - decreasing = true - else - pop!(checkedSections) - println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end +# if sections[1][:s_start] < sections[1][:s_end] +# increasing = true +# elseif sections[1][:s_start] > sections[1][:s_end] +# decreasing = true +# else +# pop!(checkedSections) +# println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") +# end - for sectionNr in 2:length(sections) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) - # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values - #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real - # defaultEnd = sections[sectionNr+1][:s_start] - #end - defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) +# for sectionNr in 2:length(sections) +# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) +# # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values +# #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real +# # defaultEnd = sections[sectionNr+1][:s_start] +# #end +# defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! +# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) +# checkAndSetPositiveNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) +# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - push!(checkedSections, sections[sectionNr]) +# push!(checkedSections, sections[sectionNr]) - # compare the section's start and end position - if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] - increasing = true - elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] - decreasing = true - else - pop!(checkedSections) - println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end - if increasing && decreasing - error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") - end +# # compare the section's start and end position +# if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] +# increasing = true +# elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] +# decreasing = true +# else +# pop!(checkedSections) +# println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") +# end +# if increasing && decreasing +# error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") +# end - if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] - error("ERROR at checking the input for the path[:sections]: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") - # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? - end - end #for +# if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] +# error("ERROR at checking the input for the path.sections: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") +# # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? +# end +# end #for - return path -end #function checkAndSetSections! +# return path +# end #function checkAndSetSections! -function createSections!(path::Dict, key::Symbol) - # read the section starting positions and corresponding information - if key == :sectionStarts - sectionStartsArray = path[:sectionStarts] - conversionFactor = 1.0 # conversion factor between the units m/s and m/s +# function createSections!(path::Path, key::Symbol) +# # read the section starting positions and corresponding information +# if key == :sectionStarts +# sectionStartsArray = path[:sectionStarts] +# conversionFactor = 1.0 # conversion factor between the units m/s and m/s - if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) - end - elseif key == :sectionStarts_kmh - sectionStartsArray = path[:sectionStarts_kmh] - conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s - else - error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") - end # if +# if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing +# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) +# end +# elseif key == :sectionStarts_kmh +# sectionStartsArray = path[:sectionStarts_kmh] +# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s +# elseif key == :characteristic_sections +# sectionStartsArray = path[:characteristic_sections] +# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s +# else +# error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") +# end # if - # check if the array is correct and if elements of the array have the correct type and valid values - errorDetected = false - if length(sectionStartsArray)<2 - error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") - end +# # check if the array is correct and if elements of the array have the correct type and valid values +# errorDetected = false +# if length(sectionStartsArray)<2 +# error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") +# end - for row in 1:length(sectionStartsArray) - if length(sectionStartsArray[row])>=3 - if length(sectionStartsArray[row])>3 - println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") - end - else - error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") - end +# for row in 1:length(sectionStartsArray) +# if length(sectionStartsArray[row])>=3 +# if length(sectionStartsArray[row])>3 +# println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") +# end +# else +# error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") +# end - if !(typeof(sectionStartsArray[row][1]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") - end - if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) - errorDetected=true - println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") - end - if !(typeof(sectionStartsArray[row][3]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") - end - end # for - if errorDetected - error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") - end +# if !(typeof(sectionStartsArray[row][1]) <: Real) +# errorDetected=true +# println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") +# end +# if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) +# errorDetected=true +# println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") +# end +# if !(typeof(sectionStartsArray[row][3]) <: Real) +# errorDetected=true +# println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") +# end +# end # for +# if errorDetected +# error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") +# end - sections = [] - for row in 2:length(sectionStartsArray) - s_start = sectionStartsArray[row-1][1] # first point of the section (in m) - s_end = sectionStartsArray[row][1] # first point of the next section (in m) - v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) - f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) +# sections = [] +# for row in 2:length(sectionStartsArray) +# s_start = sectionStartsArray[row-1][1] # first point of the section (in m) +# s_end = sectionStartsArray[row][1] # first point of the next section (in m) +# v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) +# f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) - section = Dict(:s_start => s_start, - :s_end => s_end, - :v_limit => v_limit, - :f_Rp => f_Rp) - push!(sections, section) - end # for - # s_start in first entry defines the path's beginning - # s_end in last entry defines the path's ending +# section = Dict(:s_start => s_start, +# :s_end => s_end, +# :v_limit => v_limit, +# :f_Rp => f_Rp) +# push!(sections, section) +# end # for +# # s_start in first entry defines the path's beginning +# # s_end in last entry defines the path's ending - merge!(path, Dict(:sections => sections)) - return path -end #function createSections! +# merge!(path, Dict(:sections => sections)) +# return path +# end #function createSections! -function checkAndSetPOIs!(path::Dict) - # read the section starting positions and corresponding information - if haskey(path, :pointsOfInterest) - if path[:pointsOfInterest] != nothing - pointsOfInterest = path[:pointsOfInterest] +# function checkAndSetPOIs!(path::Path) +# # read the section starting positions and corresponding information +# if haskey(path, :pointsOfInterest) +# # if path.poi != nothing +# pointsOfInterest = path[:points_of_interest] - sortingNeeded = false - errorDetected = false - for element in 1:length(pointsOfInterest) - if typeof(pointsOfInterest[element]) <: Real - if element > 1 - if pointsOfInterest[element] < pointsOfInterest[element-1] - sortingNeeded = true - println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") - end - end - else - errorDetected = true - println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") - end - end # for +# sortingNeeded = false +# errorDetected = false +# for element in 1:length(pointsOfInterest) +# if typeof(pointsOfInterest[element]) <: Real +# if element > 1 +# if pointsOfInterest[element] < pointsOfInterest[element-1] +# sortingNeeded = true +# println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") +# end +# end +# else +# errorDetected = true +# println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") +# end +# end # for - if errorDetected - error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") - end - if sortingNeeded == true - sort!(pointsOfInterest) - end +# if errorDetected +# error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") +# end +# if sortingNeeded == true +# sort!(pointsOfInterest) +# end - copiedPOIs = [] - for element in 1:length(pointsOfInterest) - if element == 1 - push!(copiedPOIs, pointsOfInterest[element]) - elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] - push!(copiedPOIs, pointsOfInterest[element]) - end - end # for - path[:pointsOfInterest] = copiedPOIs +# copiedPOIs = [] +# for element in 1:length(pointsOfInterest) +# if element == 1 +# push!(copiedPOIs, pointsOfInterest[element]) +# elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] +# push!(copiedPOIs, pointsOfInterest[element]) +# end +# end # for +# path[:points_of_interest ] = copiedPOIs - else - println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") - delete!(path, :pointsOfInterest) - end - end +# # else +# # println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") +# # delete!(path, :points_of_interest) +# # end +# end - return path -end #function checkAndSetPOIs! +# return path +# end #function checkAndSetPOIs! #function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool diff --git a/test/data/paths/const.yaml b/test/data/paths/const.yaml index 7b21c6b..94ec59e 100644 --- a/test/data/paths/const.yaml +++ b/test/data/paths/const.yaml @@ -2,8 +2,18 @@ --- path: name: "10 km, no gradient, 160 km/h" - pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m - sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰] - sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰] - - [0, 160, 0] - - [10000, 160, 0] + id: const + UUID: 23ff336e-9b9a-4535-bdb6-9db488b10945 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 999.00, point_1, front ] + - [ 2000.00, point_2, front ] + - [ 3333.30, point_3, rear ] + - [ 5000.00, point_4, front ] + - [ 7777.00, point_5, front ] + - [ 9000.00, point_6, front ] + - [ 9500.95, point_7, front ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 10000.0, 160, 0.00 ] diff --git a/test/data/paths/realworld.yaml b/test/data/paths/realworld.yaml index aa5cf35..f8d7925 100644 --- a/test/data/paths/realworld.yaml +++ b/test/data/paths/realworld.yaml @@ -2,353 +2,359 @@ --- path: name: "'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5" - # source: https://www.railml.org/en/user/exampledata.html -> "Real world railway examples from professional tools" -> "East Saxony railway network by FBS" -> "Ostsachsen_V220.railml" -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5 - sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰] - sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰] - - [0.0, 40, 0] - - [318.0, 40, 2] - - [399.0, 40, -3] - - [500.0, 40, 0] - - [579.0, 40, 1] - - [784.0, 40, 5.3] - - [868.0, 40, 20] - - [1082.0, 40, 16.1] - - [1287.0, 40, 18.1] - - [1800.0, 110, 18.1] - - [2242.0, 110, 15.4] - - [3295.0, 110, 11] - - [3880.0, 110, 11.1] - - [4680.0, 45, 11.1] - - [4686.0, 90, 11.1] - - [6122.0, 90, 0] - - [6487.0, 90, 1.5] - - [6588.0, 70, 1.5] - - [6589.0, 70, 2.4] - - [6608.0, 130, 2.4] - - [6723.0, 150, 1.3] - - [6928.0, 160, 1.3] - - [7030.0, 160, 7.5] - - [7300.0, 160, 8] - - [7552.0, 160, 5] - - [7675.0, 160, 6.7] - - [7800.0, 160, 7.4] - - [7920.0, 160, 7] - - [8020.0, 140, 7] - - [8100.0, 140, 6.3] - - [8168.0, 140, 7.4] - - [8226.0, 140, 8.4] - - [8300.0, 140, 7.4] - - [8381.0, 150, 7.4] - - [8600.0, 150, 7.8] - - [8900.0, 150, 6.7] - - [9100.0, 150, 7.8] - - [9600.0, 150, 8] - - [9845.0, 150, 7.3] - - [10005.0, 160, 7.3] - - [10600.0, 160, 6] - - [10748.0, 160, 7.3] - - [11100.0, 160, 4.5] - - [11280.0, 160, 3.6] - - [11450.0, 160, 7.2] - - [11800.0, 160, 6.8] - - [12100.0, 160, 7.3] - - [12590.0, 160, 7.6] - - [13100.0, 160, 7.3] - - [13500.0, 160, 7.1] - - [13800.0, 160, 7.3] - - [14138.0, 150, 7.3] - - [14330.0, 150, 0.3] - - [14640.0, 150, -1.8] - - [14764.0, 160, -2.8] - - [15000.0, 160, -3.3] - - [15500.0, 160, -0.9] - - [16000.0, 160, 0] - - [16470.0, 160, 1.2] - - [16572.0, 160, 2.2] - - [16700.0, 160, 3.8] - - [16949.0, 160, 3.5] - - [17086.0, 160, 3.2] - - [17232.0, 160, 1.3] - - [17339.0, 160, 4.5] - - [17406.0, 160, 3.4] - - [17727.0, 150, 3.4] - - [17807.0, 150, 4.6] - - [18049.0, 150, 3] - - [18210.0, 140, 3] - - [18300.0, 140, 4.6] - - [18680.0, 140, 3.2] - - [18761.0, 150, 3.2] - - [19047.0, 150, 5.1] - - [19305.0, 150, 3] - - [19406.0, 160, 3] - - [19414.0, 160, -0.3] - - [19900.0, 160, 3] - - [20150.0, 160, 1.8] - - [20470.0, 160, -4] - - [20940.0, 160, -3.6] - - [21150.0, 160, -1.5] - - [21390.0, 160, 0] - - [21702.0, 160, 1.5] - - [22188.0, 150, 1.5] - - [22294.0, 150, 1.8] - - [22383.0, 160, 1.8] - - [22500.0, 160, 2.6] - - [22900.0, 160, 4.6] - - [23542.0, 160, 0.2] - - [23736.0, 160, 0.9] - - [24124.0, 160, 7] - - [24918.0, 160, 7.6] - - [25100.0, 150, 7.1] - - [25580.0, 150, 7.4] - - [25708.0, 160, 7.4] - - [25810.0, 160, 7.1] - - [26040.0, 160, 3.2] - - [26330.0, 160, 1.8] - - [26593.0, 160, 2.1] - - [27020.0, 160, 3.5] - - [27195.0, 160, 5.8] - - [27253.0, 160, 1.1] - - [27310.0, 160, 3.5] - - [27595.0, 160, 3.4] - - [28530.0, 160, 4.6] - - [29115.0, 160, 0] - - [29700.0, 160, -5.2] - - [30055.0, 120, -4.3] - - [30301.0, 120, -6.2] - - [30487.0, 160, -6.2] - - [30537.0, 160, -4.8] - - [31130.0, 160, -0.3] - - [31293.0, 160, 3.7] - - [31400.0, 160, 2.9] - - [31640.0, 160, 4.1] - - [31795.0, 120, 4.1] - - [32010.0, 120, 3.2] - - [32138.0, 130, 3.2] - - [32365.0, 130, -4] - - [33000.0, 130, 6.1] - - [33426.0, 160, 6.1] - - [33907.0, 160, 7.1] - - [34220.0, 160, 8.4] - - [34300.0, 160, 7.9] - - [34440.0, 160, 7.1] - - [34610.0, 160, 4.4] - - [35000.0, 160, 5.6] - - [35173.0, 150, 5.6] - - [35400.0, 150, -0.2] - - [35597.0, 160, -0.2] - - [35900.0, 160, 0] - - [36700.0, 160, 3.9] - - [36938.0, 160, 0] - - [37700.0, 160, -0.3] - - [37978.0, 150, -5.6] - - [38063.0, 150, -1.9] - - [38141.0, 150, -3.1] - - [38210.0, 150, 0] - - [38406.0, 150, -7] - - [38900.0, 150, -7.5] - - [39200.0, 150, -8.7] - - [39298.0, 150, -6.4] - - [39476.0, 150, -7.2] - - [40400.0, 150, -6.9] - - [40676.0, 130, -6.9] - - [41000.0, 130, -7.3] - - [41406.0, 130, -7.5] - - [41571.0, 160, -7.3] - - [41816.0, 160, -6.8] - - [41923.0, 160, -7.1] - - [42139.0, 160, -1.3] - - [42343.0, 160, -2.4] - - [42432.0, 150, -2.4] - - [42952.0, 160, -2.4] - - [43000.0, 160, -4.6] - - [43264.0, 160, -2.5] - - [43388.0, 160, -0.6] - - [43700.0, 160, 0.8] - - [44030.0, 160, 2.7] - - [44430.0, 160, 0] - - [44708.0, 160, -7.2] - - [45477.0, 160, -7.8] - - [45890.0, 160, -1] - - [46562.0, 160, -1.6] - - [47000.0, 160, -6.9] - - [47500.0, 160, -7.7] - - [47800.0, 160, -6.8] - - [48700.0, 160, -4.3] - - [49218.0, 160, -5.3] - - [49514.0, 160, -2.5] - - [50000.0, 160, -2.2] - - [51150.0, 160, -5.7] - - [51406.0, 160, -6.3] - - [51710.0, 150, -5.8] - - [52000.0, 150, -6.7] - - [52215.0, 150, -6.1] - - [53213.0, 150, -6.8] - - [53567.0, 150, -5.6] - - [53943.0, 150, -6.5] - - [54129.0, 140, -6.5] - - [54212.0, 140, -10.5] - - [54247.0, 140, -5.4] - - [54326.0, 140, -6.1] - - [54450.0, 140, -6.5] - - [54482.0, 120, -6.5] - - [54550.0, 120, -6.2] - - [54855.0, 140, -6.2] - - [55307.0, 140, -0.3] - - [55651.0, 140, -1.2] - - [55788.0, 140, 0] - - [55918.0, 100, 0] - - [56433.0, 150, 0] - - [56560.0, 150, -2.1] - - [56624.0, 150, -6.7] - - [57012.0, 150, 1.3] - - [57260.0, 150, 6.6] - - [57800.0, 150, 5.3] - - [57987.0, 150, 0] - - [57990.0, 150, 7.6] - - [58321.0, 150, 6.4] - - [59090.0, 150, 6.9] - - [59468.0, 150, 9.2] - - [59600.0, 150, 0.2] - - [60683.0, 150, 4.1] - - [61156.0, 150, 2.3] - - [61181.0, 130, 2.3] - - [61325.0, 130, 0] - - [61605.0, 130, 7.3] - - [62108.0, 150, 7.3] - - [62246.0, 150, 6.6] - - [62279.0, 150, 5.1] - - [62454.0, 150, 0.9] - - [62777.0, 150, 5.5] - - [63802.0, 150, 4.6] - - [64344.0, 150, 0.2] - - [64932.0, 150, -0.6] - - [65100.0, 150, 0] - - [65690.0, 150, 1.8] - - [65878.0, 150, 2.5] - - [66266.0, 150, -1] - - [66339.0, 150, 6.3] - - [66448.0, 160, 6.3] - - [66587.0, 160, 0] - - [66856.0, 160, 3.2] - - [67480.0, 160, 3.6] - - [67697.0, 160, 2.2] - - [67800.0, 160, 6] - - [67851.0, 130, 6] - - [68027.0, 130, 2.7] - - [68172.0, 130, 0.6] - - [68328.0, 130, 2.5] - - [68357.0, 130, 0] - - [68479.0, 130, 7.1] - - [68783.0, 130, 7.4] - - [69056.0, 150, 7.4] - - [69500.0, 150, 6.8] - - [69741.0, 160, 6.8] - - [69900.0, 160, 6.6] - - [70757.0, 160, 3.6] - - [71384.0, 160, 6] - - [71568.0, 160, 7.1] - - [71800.0, 160, 7.4] - - [72100.0, 160, 7.3] - - [73919.0, 150, 7.3] - - [74317.0, 140, 7.3] - - [74448.0, 140, 1.5] - - [74590.0, 140, -1.5] - - [74620.0, 140, -5] - - [74950.0, 140, -4.3] - - [75100.0, 140, -1.8] - - [75154.0, 130, -1.8] - - [75260.0, 130, -6.2] - - [75873.0, 130, -5.6] - - [76062.0, 100, -5.6] - - [76100.0, 100, -6.4] - - [76350.0, 100, -5.7] - - [76476.0, 100, -7] - - [76600.0, 100, -6.4] - - [76601.0, 90, -6.4] - - [76726.0, 90, -6.2] - - [77256.0, 90, -2.1] - - [77285.0, 80, -2.1] - - [77299.0, 80, -14] - - [77331.0, 80, -1.6] - - [77379.0, 90, -1.6] - - [77425.0, 110, -1.6] - - [77455.0, 110, -4.9] - - [77498.0, 110, 0] - - [77505.0, 160, 0] - - [77555.0, 160, -5.6] - - [77662.0, 160, 0] - - [78085.0, 160, -4] - - [78223.0, 160, -6.8] - - [78337.0, 130, -6.8] - - [78856.0, 130, -6.6] - - [78875.0, 130, -7.2] - - [79345.0, 150, -7.2] - - [79600.0, 150, -6.5] - - [79792.0, 150, -0.2] - - [80537.0, 150, -5.2] - - [81300.0, 150, -4.8] - - [81634.0, 110, -4.8] - - [81943.0, 110, -5.4] - - [82166.0, 110, 0] - - [82408.0, 110, 4.8] - - [82790.0, 110, 5.9] - - [83137.0, 120, 5.9] - - [83300.0, 120, 0] - - [83519.0, 150, 0] - - [83597.0, 150, -7.8] - - [83827.0, 150, -8.1] - - [84150.0, 150, -7.2] - - [84391.0, 150, 0] - - [84966.0, 150, -4] - - [85529.0, 130, -2.3] - - [85589.0, 130, -4] - - [86081.0, 130, 0] - - [86514.0, 130, 7.8] - - [86577.0, 120, 7.8] - - [87554.0, 90, 7.8] - - [87690.0, 90, 7.9] - - [87842.0, 90, 1.7] - - [88007.0, 110, 1.7] - - [88100.0, 110, 3.4] - - [88260.0, 110, 5.3] - - [88376.0, 160, 5.3] - - [88450.0, 160, 6.7] - - [89050.0, 160, 7.4] - - [89350.0, 160, -7.1] - - [90365.0, 160, 0] - - [90700.0, 160, -7.3] - - [92000.0, 160, -7.9] - - [92166.0, 160, -4] - - [92460.0, 160, -5.4] - - [93330.0, 160, 0] - - [93901.0, 160, 0.7] - - [94156.0, 160, 5.2] - - [94440.0, 160, -2.8] - - [94530.0, 160, -0.8] - - [94630.0, 160, -6.8] - - [94830.0, 160, -4.4] - - [95090.0, 160, -4.6] - - [95500.0, 160, -4.8] - - [96500.0, 160, -4.4] - - [96700.0, 160, -5.6] - - [97000.0, 160, -4.6] - - [97590.0, 160, 0] - - [97858.0, 120, 0] - - [98224.0, 120, -1.5] - - [98264.0, 120, 0] - - [98577.0, 120, 7.5] - - [98738.0, 120, 0] - - [99055.0, 130, 0] - - [99427.0, 130, -2] - - [99610.0, 130, -3.1] - - [99906.0, 120, -3.1] - - [99980.0, 120, -1.3] - - [100190.0, 120, -6.8] - - [100832.0, 120, -7.2] - - [100980.0, 120, -8.1] - - [101100.0, 120, -7.4] - - [101245.0, 120, -6.3] - - [101332.0, 100, -6.3] - - [101365.0, 100, -2.4] - - [101551.0, 110, -2.4] - - [101800.0, 110, 0] + id: realworld + UUID: 2b31a0c5-85bc-4721-b7e0-66f9df95f7b6 + # source: https://www.railml.org/en/user/exampledata.html + # -> "Real world railway examples from professional tools" + # -> "East Saxony railway network by FBS" + # -> "Ostsachsen_V220.railml" + # -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5 + characteristic_sections: + # [ s in m, v_limit in km/h, f_Rp in ‰ ] + - [ 0.0, 40, 0.0 ] + - [ 318.0, 40, 2.0 ] + - [ 399.0, 40, -3.0 ] + - [ 500.0, 40, 0.0 ] + - [ 579.0, 40, 1.0 ] + - [ 784.0, 40, 5.3 ] + - [ 868.0, 40, 20.0 ] + - [ 1082.0, 40, 16.1 ] + - [ 1287.0, 40, 18.1 ] + - [ 1800.0, 110, 18.1 ] + - [ 2242.0, 110, 15.4 ] + - [ 3295.0, 110, 11.0 ] + - [ 3880.0, 110, 11.1 ] + - [ 4680.0, 45, 11.1 ] + - [ 4686.0, 90, 11.1 ] + - [ 6122.0, 90, 0.0 ] + - [ 6487.0, 90, 1.5 ] + - [ 6588.0, 70, 1.5 ] + - [ 6589.0, 70, 2.4 ] + - [ 6608.0, 130, 2.4 ] + - [ 6723.0, 150, 1.3 ] + - [ 6928.0, 160, 1.3 ] + - [ 7030.0, 160, 7.5 ] + - [ 7300.0, 160, 8.0 ] + - [ 7552.0, 160, 5.0 ] + - [ 7675.0, 160, 6.7 ] + - [ 7800.0, 160, 7.4 ] + - [ 7920.0, 160, 7.0 ] + - [ 8020.0, 140, 7.0 ] + - [ 8100.0, 140, 6.3 ] + - [ 8168.0, 140, 7.4 ] + - [ 8226.0, 140, 8.4 ] + - [ 8300.0, 140, 7.4 ] + - [ 8381.0, 150, 7.4 ] + - [ 8600.0, 150, 7.8 ] + - [ 8900.0, 150, 6.7 ] + - [ 9100.0, 150, 7.8 ] + - [ 9600.0, 150, 8.0 ] + - [ 9845.0, 150, 7.3 ] + - [ 10005.0, 160, 7.3 ] + - [ 10600.0, 160, 6.0 ] + - [ 10748.0, 160, 7.3 ] + - [ 11100.0, 160, 4.5 ] + - [ 11280.0, 160, 3.6 ] + - [ 11450.0, 160, 7.2 ] + - [ 11800.0, 160, 6.8 ] + - [ 12100.0, 160, 7.3 ] + - [ 12590.0, 160, 7.6 ] + - [ 13100.0, 160, 7.3 ] + - [ 13500.0, 160, 7.1 ] + - [ 13800.0, 160, 7.3 ] + - [ 14138.0, 150, 7.3 ] + - [ 14330.0, 150, 0.3 ] + - [ 14640.0, 150, -1.8 ] + - [ 14764.0, 160, -2.8 ] + - [ 15000.0, 160, -3.3 ] + - [ 15500.0, 160, -0.9 ] + - [ 16000.0, 160, 0.0 ] + - [ 16470.0, 160, 1.2 ] + - [ 16572.0, 160, 2.2 ] + - [ 16700.0, 160, 3.8 ] + - [ 16949.0, 160, 3.5 ] + - [ 17086.0, 160, 3.2 ] + - [ 17232.0, 160, 1.3 ] + - [ 17339.0, 160, 4.5 ] + - [ 17406.0, 160, 3.4 ] + - [ 17727.0, 150, 3.4 ] + - [ 17807.0, 150, 4.6 ] + - [ 18049.0, 150, 3.0 ] + - [ 18210.0, 140, 3.0 ] + - [ 18300.0, 140, 4.6 ] + - [ 18680.0, 140, 3.2 ] + - [ 18761.0, 150, 3.2 ] + - [ 19047.0, 150, 5.1 ] + - [ 19305.0, 150, 3.0 ] + - [ 19406.0, 160, 3.0 ] + - [ 19414.0, 160, -0.3 ] + - [ 19900.0, 160, 3.0 ] + - [ 20150.0, 160, 1.8 ] + - [ 20470.0, 160, -4.0 ] + - [ 20940.0, 160, -3.6 ] + - [ 21150.0, 160, -1.5 ] + - [ 21390.0, 160, 0.0 ] + - [ 21702.0, 160, 1.5 ] + - [ 22188.0, 150, 1.5 ] + - [ 22294.0, 150, 1.8 ] + - [ 22383.0, 160, 1.8 ] + - [ 22500.0, 160, 2.6 ] + - [ 22900.0, 160, 4.6 ] + - [ 23542.0, 160, 0.2 ] + - [ 23736.0, 160, 0.9 ] + - [ 24124.0, 160, 7.0 ] + - [ 24918.0, 160, 7.6 ] + - [ 25100.0, 150, 7.1 ] + - [ 25580.0, 150, 7.4 ] + - [ 25708.0, 160, 7.4 ] + - [ 25810.0, 160, 7.1 ] + - [ 26040.0, 160, 3.2 ] + - [ 26330.0, 160, 1.8 ] + - [ 26593.0, 160, 2.1 ] + - [ 27020.0, 160, 3.5 ] + - [ 27195.0, 160, 5.8 ] + - [ 27253.0, 160, 1.1 ] + - [ 27310.0, 160, 3.5 ] + - [ 27595.0, 160, 3.4 ] + - [ 28530.0, 160, 4.6 ] + - [ 29115.0, 160, 0.0 ] + - [ 29700.0, 160, -5.2 ] + - [ 30055.0, 120, -4.3 ] + - [ 30301.0, 120, -6.2 ] + - [ 30487.0, 160, -6.2 ] + - [ 30537.0, 160, -4.8 ] + - [ 31130.0, 160, -0.3 ] + - [ 31293.0, 160, 3.7 ] + - [ 31400.0, 160, 2.9 ] + - [ 31640.0, 160, 4.1 ] + - [ 31795.0, 120, 4.1 ] + - [ 32010.0, 120, 3.2 ] + - [ 32138.0, 130, 3.2 ] + - [ 32365.0, 130, -4.0 ] + - [ 33000.0, 130, 6.1 ] + - [ 33426.0, 160, 6.1 ] + - [ 33907.0, 160, 7.1 ] + - [ 34220.0, 160, 8.4 ] + - [ 34300.0, 160, 7.9 ] + - [ 34440.0, 160, 7.1 ] + - [ 34610.0, 160, 4.4 ] + - [ 35000.0, 160, 5.6 ] + - [ 35173.0, 150, 5.6 ] + - [ 35400.0, 150, -0.2 ] + - [ 35597.0, 160, -0.2 ] + - [ 35900.0, 160, 0.0 ] + - [ 36700.0, 160, 3.9 ] + - [ 36938.0, 160, 0.0 ] + - [ 37700.0, 160, -0.3 ] + - [ 37978.0, 150, -5.6 ] + - [ 38063.0, 150, -1.9 ] + - [ 38141.0, 150, -3.1 ] + - [ 38210.0, 150, 0.0 ] + - [ 38406.0, 150, -7.0 ] + - [ 38900.0, 150, -7.5 ] + - [ 39200.0, 150, -8.7 ] + - [ 39298.0, 150, -6.4 ] + - [ 39476.0, 150, -7.2 ] + - [ 40400.0, 150, -6.9 ] + - [ 40676.0, 130, -6.9 ] + - [ 41000.0, 130, -7.3 ] + - [ 41406.0, 130, -7.5 ] + - [ 41571.0, 160, -7.3 ] + - [ 41816.0, 160, -6.8 ] + - [ 41923.0, 160, -7.1 ] + - [ 42139.0, 160, -1.3 ] + - [ 42343.0, 160, -2.4 ] + - [ 42432.0, 150, -2.4 ] + - [ 42952.0, 160, -2.4 ] + - [ 43000.0, 160, -4.6 ] + - [ 43264.0, 160, -2.5 ] + - [ 43388.0, 160, -0.6 ] + - [ 43700.0, 160, 0.8 ] + - [ 44030.0, 160, 2.7 ] + - [ 44430.0, 160, 0.0 ] + - [ 44708.0, 160, -7.2 ] + - [ 45477.0, 160, -7.8 ] + - [ 45890.0, 160, -1.0 ] + - [ 46562.0, 160, -1.6 ] + - [ 47000.0, 160, -6.9 ] + - [ 47500.0, 160, -7.7 ] + - [ 47800.0, 160, -6.8 ] + - [ 48700.0, 160, -4.3 ] + - [ 49218.0, 160, -5.3 ] + - [ 49514.0, 160, -2.5 ] + - [ 50000.0, 160, -2.2 ] + - [ 51150.0, 160, -5.7 ] + - [ 51406.0, 160, -6.3 ] + - [ 51710.0, 150, -5.8 ] + - [ 52000.0, 150, -6.7 ] + - [ 52215.0, 150, -6.1 ] + - [ 53213.0, 150, -6.8 ] + - [ 53567.0, 150, -5.6 ] + - [ 53943.0, 150, -6.5 ] + - [ 54129.0, 140, -6.5 ] + - [ 54212.0, 140, -10.5 ] + - [ 54247.0, 140, -5.4 ] + - [ 54326.0, 140, -6.1 ] + - [ 54450.0, 140, -6.5 ] + - [ 54482.0, 120, -6.5 ] + - [ 54550.0, 120, -6.2 ] + - [ 54855.0, 140, -6.2 ] + - [ 55307.0, 140, -0.3 ] + - [ 55651.0, 140, -1.2 ] + - [ 55788.0, 140, 0.0 ] + - [ 55918.0, 100, 0.0 ] + - [ 56433.0, 150, 0.0 ] + - [ 56560.0, 150, -2.1 ] + - [ 56624.0, 150, -6.7 ] + - [ 57012.0, 150, 1.3 ] + - [ 57260.0, 150, 6.6 ] + - [ 57800.0, 150, 5.3 ] + - [ 57987.0, 150, 0.0 ] + - [ 57990.0, 150, 7.6 ] + - [ 58321.0, 150, 6.4 ] + - [ 59090.0, 150, 6.9 ] + - [ 59468.0, 150, 9.2 ] + - [ 59600.0, 150, 0.2 ] + - [ 60683.0, 150, 4.1 ] + - [ 61156.0, 150, 2.3 ] + - [ 61181.0, 130, 2.3 ] + - [ 61325.0, 130, 0.0 ] + - [ 61605.0, 130, 7.3 ] + - [ 62108.0, 150, 7.3 ] + - [ 62246.0, 150, 6.6 ] + - [ 62279.0, 150, 5.1 ] + - [ 62454.0, 150, 0.9 ] + - [ 62777.0, 150, 5.5 ] + - [ 63802.0, 150, 4.6 ] + - [ 64344.0, 150, 0.2 ] + - [ 64932.0, 150, -0.6 ] + - [ 65100.0, 150, 0.0 ] + - [ 65690.0, 150, 1.8 ] + - [ 65878.0, 150, 2.5 ] + - [ 66266.0, 150, -1.0 ] + - [ 66339.0, 150, 6.3 ] + - [ 66448.0, 160, 6.3 ] + - [ 66587.0, 160, 0.0 ] + - [ 66856.0, 160, 3.2 ] + - [ 67480.0, 160, 3.6 ] + - [ 67697.0, 160, 2.2 ] + - [ 67800.0, 160, 6.0 ] + - [ 67851.0, 130, 6.0 ] + - [ 68027.0, 130, 2.7 ] + - [ 68172.0, 130, 0.6 ] + - [ 68328.0, 130, 2.5 ] + - [ 68357.0, 130, 0.0 ] + - [ 68479.0, 130, 7.1 ] + - [ 68783.0, 130, 7.4 ] + - [ 69056.0, 150, 7.4 ] + - [ 69500.0, 150, 6.8 ] + - [ 69741.0, 160, 6.8 ] + - [ 69900.0, 160, 6.6 ] + - [ 70757.0, 160, 3.6 ] + - [ 71384.0, 160, 6.0 ] + - [ 71568.0, 160, 7.1 ] + - [ 71800.0, 160, 7.4 ] + - [ 72100.0, 160, 7.3 ] + - [ 73919.0, 150, 7.3 ] + - [ 74317.0, 140, 7.3 ] + - [ 74448.0, 140, 1.5 ] + - [ 74590.0, 140, -1.5 ] + - [ 74620.0, 140, -5.0 ] + - [ 74950.0, 140, -4.3 ] + - [ 75100.0, 140, -1.8 ] + - [ 75154.0, 130, -1.8 ] + - [ 75260.0, 130, -6.2 ] + - [ 75873.0, 130, -5.6 ] + - [ 76062.0, 100, -5.6 ] + - [ 76100.0, 100, -6.4 ] + - [ 76350.0, 100, -5.7 ] + - [ 76476.0, 100, -7.0 ] + - [ 76600.0, 100, -6.4 ] + - [ 76601.0, 90, -6.4 ] + - [ 76726.0, 90, -6.2 ] + - [ 77256.0, 90, -2.1 ] + - [ 77285.0, 80, -2.1 ] + - [ 77299.0, 80, -14.0 ] + - [ 77331.0, 80, -1.6 ] + - [ 77379.0, 90, -1.6 ] + - [ 77425.0, 110, -1.6 ] + - [ 77455.0, 110, -4.9 ] + - [ 77498.0, 110, 0.0 ] + - [ 77505.0, 160, 0.0 ] + - [ 77555.0, 160, -5.6 ] + - [ 77662.0, 160, 0.0 ] + - [ 78085.0, 160, -4.0 ] + - [ 78223.0, 160, -6.8 ] + - [ 78337.0, 130, -6.8 ] + - [ 78856.0, 130, -6.6 ] + - [ 78875.0, 130, -7.2 ] + - [ 79345.0, 150, -7.2 ] + - [ 79600.0, 150, -6.5 ] + - [ 79792.0, 150, -0.2 ] + - [ 80537.0, 150, -5.2 ] + - [ 81300.0, 150, -4.8 ] + - [ 81634.0, 110, -4.8 ] + - [ 81943.0, 110, -5.4 ] + - [ 82166.0, 110, 0.0 ] + - [ 82408.0, 110, 4.8 ] + - [ 82790.0, 110, 5.9 ] + - [ 83137.0, 120, 5.9 ] + - [ 83300.0, 120, 0.0 ] + - [ 83519.0, 150, 0.0 ] + - [ 83597.0, 150, -7.8 ] + - [ 83827.0, 150, -8.1 ] + - [ 84150.0, 150, -7.2 ] + - [ 84391.0, 150, 0.0 ] + - [ 84966.0, 150, -4.0 ] + - [ 85529.0, 130, -2.3 ] + - [ 85589.0, 130, -4.0 ] + - [ 86081.0, 130, 0.0 ] + - [ 86514.0, 130, 7.8 ] + - [ 86577.0, 120, 7.8 ] + - [ 87554.0, 90, 7.8 ] + - [ 87690.0, 90, 7.9 ] + - [ 87842.0, 90, 1.7 ] + - [ 88007.0, 110, 1.7 ] + - [ 88100.0, 110, 3.4 ] + - [ 88260.0, 110, 5.3 ] + - [ 88376.0, 160, 5.3 ] + - [ 88450.0, 160, 6.7 ] + - [ 89050.0, 160, 7.4 ] + - [ 89350.0, 160, -7.1 ] + - [ 90365.0, 160, 0.0 ] + - [ 90700.0, 160, -7.3 ] + - [ 92000.0, 160, -7.9 ] + - [ 92166.0, 160, -4.0 ] + - [ 92460.0, 160, -5.4 ] + - [ 93330.0, 160, 0.0 ] + - [ 93901.0, 160, 0.7 ] + - [ 94156.0, 160, 5.2 ] + - [ 94440.0, 160, -2.8 ] + - [ 94530.0, 160, -0.8 ] + - [ 94630.0, 160, -6.8 ] + - [ 94830.0, 160, -4.4 ] + - [ 95090.0, 160, -4.6 ] + - [ 95500.0, 160, -4.8 ] + - [ 96500.0, 160, -4.4 ] + - [ 96700.0, 160, -5.6 ] + - [ 97000.0, 160, -4.6 ] + - [ 97590.0, 160, 0.0 ] + - [ 97858.0, 120, 0.0 ] + - [ 98224.0, 120, -1.5 ] + - [ 98264.0, 120, 0.0 ] + - [ 98577.0, 120, 7.5 ] + - [ 98738.0, 120, 0.0 ] + - [ 99055.0, 130, 0.0 ] + - [ 99427.0, 130, -2.0 ] + - [ 99610.0, 130, -3.1 ] + - [ 99906.0, 120, -3.1 ] + - [ 99980.0, 120, -1.3 ] + - [100190.0, 120, -6.8 ] + - [100832.0, 120, -7.2 ] + - [100980.0, 120, -8.1 ] + - [101100.0, 120, -7.4 ] + - [101245.0, 120, -6.3 ] + - [101332.0, 100, -6.3 ] + - [101365.0, 100, -2.4 ] + - [101551.0, 110, -2.4 ] + - [101800.0, 110, 0.0 ] diff --git a/test/data/paths/slope.yaml b/test/data/paths/slope.yaml index 28cdad6..5922df6 100644 --- a/test/data/paths/slope.yaml +++ b/test/data/paths/slope.yaml @@ -2,18 +2,26 @@ --- path: name: "10 km, different gradient, 160 km/h" - pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m - sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰] - sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰] - - [0, 160, 0] - - [1000, 160, 1] - - [2000, 160, 2] - - [3000, 160, 5] - - [4000, 160, -3] - - [5000, 160, 5] - - [6000, 160, -10] - - [7000, 160, 15] - - [8000, 160, -10] - - [8500, 160, 20] - - [9000, 160, 0] - - [10000, 160, 0] + id: slope + UUID: ffd243a9-0223-4210-8c7d-e6c90fde70d3 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 850.00, view_point_1, front ] + - [ 1000.00, distant_signal_1, front ] + - [ 2000.00, main_signal_1, front ] + - [ 9000.00, main_signal_3, front ] + - [ 9050.00, clearing_point_1, rear ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 1000.0, 160, 1.00 ] + - [ 2000.0, 160, 2.00 ] + - [ 3000.0, 160, 5.00 ] + - [ 4000.0, 160, -3.00 ] + - [ 5000.0, 160, 5.00 ] + - [ 6000.0, 160, -10.00 ] + - [ 7000.0, 160, 15.00 ] + - [ 8000.0, 160, -10.00 ] + - [ 8500.0, 160, 20.00 ] + - [ 9000.0, 160, 0.00 ] + - [ 10000.0, 160, 0.00 ] diff --git a/test/data/paths/speed.yaml b/test/data/paths/speed.yaml index 77751bd..d35e33c 100644 --- a/test/data/paths/speed.yaml +++ b/test/data/paths/speed.yaml @@ -2,16 +2,26 @@ --- path: name: "10 km, no gradient, different speed limits" - pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m - sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰] - sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰] - - [0, 160, 0.0] - - [3000, 60, 0.0] - - [4000, 160, 0.0] - - [5000, 60, 0.0] - - [6000, 160, 0.0] - - [6500, 60, 0.0] - - [6700, 65, 0.0] - - [6800, 70, 0.0] - - [7000, 120.00, 0] - - [10000, 160.00, 0.0] + id: speed + UUID: 401b8ce7-fa75-4576-8a4a-43be2eb55e50 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 999.00, point_1, front ] + - [ 2000.00, point_2, front ] + - [ 3333.30, point_3, rear ] + - [ 5000.00, point_4, front ] + - [ 7777.00, point_5, front ] + - [ 9000.00, point_6, front ] + - [ 9500.95, point_7, front ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 3000.0, 60, 0.00 ] + - [ 4000.0, 160, 0.00 ] + - [ 5000.0, 60, 0.00 ] + - [ 6000.0, 160, 0.00 ] + - [ 6500.0, 60, 0.00 ] + - [ 6700.0, 65, 0.00 ] + - [ 6800.0, 70, 0.00 ] + - [ 7000.0, 120, 0.00 ] + - [ 10000.0, 160, 0.00 ] diff --git a/test/data/settings/detail.yaml b/test/data/settings/detail.yaml deleted file mode 100644 index 7b55da7..0000000 --- a/test/data/settings/detail.yaml +++ /dev/null @@ -1,9 +0,0 @@ -%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/everything.yaml b/test/data/settings/everything.yaml new file mode 100644 index 0000000..0e2cdf1 --- /dev/null +++ b/test/data/settings/everything.yaml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +settings: + outputDetail: "everything" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything" diff --git a/test/data/settings/points_of_interest.yaml b/test/data/settings/points_of_interest.yaml new file mode 100644 index 0000000..d0784ff --- /dev/null +++ b/test/data/settings/points_of_interest.yaml @@ -0,0 +1,4 @@ +%YAML 1.2 +--- +settings: + outputDetail: "points_of_interest" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything" diff --git a/test/runtests.jl b/test/runtests.jl index 541b74b..a85821f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,63 +7,83 @@ using TrainRun, Test -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")) +trains = Dict() +paths = Dict() +settings = Dict() -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")) +@testset "load data" begin -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")) + println("testing load train data") + push!(trains, :freight => @time TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml")) + push!(trains, :local => @time TrainRun.importFromYaml(:train, "test/data/trains/local.yaml")) + push!(trains, :longdistance => @time TrainRun.importFromYaml(:train, "test/data/trains/longdistance.yaml")) -@testset "TrainRun.jl" begin + println("testing load path data") + push!(paths, :const => @time Path("test/data/paths/const.yaml")) + push!(paths, :slope => @time Path("test/data/paths/slope.yaml")) + push!(paths, :speed => @time Path("test/data/paths/speed.yaml")) + push!(paths, :realworld => @time Path("test/data/paths/realworld.yaml")) + + println("testing load settings data") + push!(settings, "default" => @time Settings()) + push!(settings, "poi" => @time Settings("test/data/settings/points_of_interest.yaml")) + push!(settings, "drivingcourse" => @time Settings("test/data/settings/driving_course.yaml")) + push!(settings, "everything" => @time Settings("test/data/settings/everything.yaml")) + push!(settings, "strip" => @time Settings("test/data/settings/strip.yaml")) + push!(settings, "time" => @time Settings("test/data/settings/time.yaml")) + push!(settings, "timestrip" => @time Settings("test/data/settings/time_strip.yaml")) + push!(settings, "velocity" => @time Settings("test/data/settings/velocity.yaml")) + push!(settings, "csvexport" => @time Settings("test/data/settings/csv_export.yaml")) + + @test typeof(first(paths)[2]) == Path + @test typeof(first(settings)[2]) == Settings + +end + +println("====================") + +tests = Base.Iterators.product(trains,paths) + +## routine to generate the anticipated Dict() +# anticipated = Dict() +# for test in tests +# println(test[1][1],"-",test[2][1]) +# result = @time trainrun(test[1][2],test[2][2]) +# push!(anticipated, Symbol(String(test[1][1]) * "_" * String(test[2][1])) => result ) +# end + +anticipated = Dict( + :default => Dict( + :longdistance_speed => 499.96109564970516, + :freight_slope => 831.4768274141168, + :local_slope => 396.99313307033276, + :longdistance_const => 328.83479381353095, + :freight_realworld => 8971.50124080998, + :longdistance_slope => 329.22915822053164, + :freight_const => 727.7969403041934, + :longdistance_realworld => 2900.1198723158523, + :local_speed => 524.3948201513945, + :local_realworld => 3443.917823618831, + :freight_speed => 733.2610572579886, + :local_const => 392.7234008268302 + ) +) + +@testset "function trainrun()" begin @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 - + for test in tests + test_name = String(test[1][1]) * "_" * String(test[2][1]) + println("testing $test_name") + @time result = trainrun(test[1][2],test[2][2]) + expected = anticipated[:default][Symbol(test_name)] + # compare result to test data set + @test isapprox(result, expected, atol=0.01) + println("--------------------") end + end + println("====================") end From 980899379d2209726740a88e4f1bee565699f786 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Wed, 4 May 2022 16:41:53 +0200 Subject: [PATCH 07/18] renamed git branch "master" into "main" --- .github/workflows/CI.yml | 2 +- CHANGELOG.md | 2 +- CONTRIBUTING.md | 10 +++++----- README.md | 2 +- docs/make.jl | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 78438f5..b6f7afc 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - master + - main tags: '*' pull_request: concurrency: diff --git a/CHANGELOG.md b/CHANGELOG.md index 32fea41..301df1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,7 +167,7 @@ Modules and variables were renamed. Proof of concept and master thesis submission. -[Unreleased]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.8...master +[Unreleased]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.8...main [0.8]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.7...v0.8 [0.7]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.6.2...v0.7 [0.6.2]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.6.1...v0.6.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3bcb403..cf5bac4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ TODO! or related issues for context, before opening a new issue * Including minimal examples is greatly appreciated * If it's a bug, or unexpected behaviour, reproducing on the latest development version - (`Pkg.add(name="TrainRun", rev="master")`) is a good gut check and can streamline the process, + (`Pkg.add(name="TrainRun", rev="main")`) is a good gut check and can streamline the process, along with including the first two lines of output from `versioninfo()` ## Style Guidelines @@ -53,13 +53,13 @@ TODO ## Git Recommendations For Pull Requests -* Avoid working from the `master` branch of your fork, creating a new branch will make it - easier if TrainRun.jl `master` branch changes and you need to update your pull request; -* All PRs and issues should be opened against the `master` branch not against the current release; +* Avoid working from the `main` branch of your fork, creating a new branch will make it + easier if TrainRun.jl `main` branch changes and you need to update your pull request; +* All PRs and issues should be opened against the `main` branch not against the current release; * Run tests of your code before sending any commit to GitHub. Only push changes when the tests of the change are passing locally. In particular note that it is not a problem if you send several commits in one push command to GitHub as CI will be run only once then; -* If any conflicts arise due to changes in TrainRun.jl `master` branch, prefer updating your pull +* If any conflicts arise due to changes in TrainRun.jl `main` branch, prefer updating your pull request branch with `git rebase` (rather than `git merge`), since the latter will introduce a merge commit that might confuse GitHub when displaying the diff of your PR, which makes your changes more difficult to review. Alternatively use conflict resolution tool available at GitHub; diff --git a/README.md b/README.md index a0aea6e..de40d17 100644 --- a/README.md +++ b/README.md @@ -1,7 +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) -[![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) +[![Build Status](https://github.com/railtoolkit/TrainRun.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/railtoolkit/TrainRun.jl/actions/workflows/CI.yml?query=branch%3Amain) ------------ diff --git a/docs/make.jl b/docs/make.jl index 4b52efb..f2bf563 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -20,5 +20,5 @@ makedocs(; deploydocs(; repo="github.com/railtoolkit/TrainRun.jl", - devbranch="master", + devbranch="development", ) From 18ec0e5c54aea6a46037f01768a4124e95677151 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Wed, 4 May 2022 16:53:58 +0200 Subject: [PATCH 08/18] renamed "TrainRun" into "TrainRuns" --- .github/workflows/CI.yml | 6 ++--- CHANGELOG.md | 31 +++++++++++++------------ CITATION.cff | 10 ++++---- CONTRIBUTING.md | 20 ++++++++-------- Project.toml | 2 +- README.md | 8 +++---- docs/Project.toml | 2 +- docs/examples/ExtendedWorkingExample.jl | 2 +- docs/examples/MinimalWorkingExample.jl | 2 +- docs/make.jl | 14 +++++------ docs/src/index.md | 8 +++---- src/{TrainRun.jl => TrainRuns.jl} | 4 ++-- test/runtests.jl | 8 +++---- 13 files changed, 59 insertions(+), 58 deletions(-) rename src/{TrainRun.jl => TrainRuns.jl} (98%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b6f7afc..f104c87 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -54,6 +54,6 @@ jobs: - run: | julia --project=docs -e ' using Documenter: DocMeta, doctest - using TrainRun - DocMeta.setdocmeta!(TrainRun, :DocTestSetup, :(using TrainRun); recursive=true) - doctest(TrainRun)' + using TrainRuns + DocMeta.setdocmeta!(TrainRuns, :DocTestSetup, :(using TrainRuns); recursive=true) + doctest(TrainRuns)' diff --git a/CHANGELOG.md b/CHANGELOG.md index 301df1f..992cad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * validation of YAML input via JSON schema ### Changed +* renamed TrainRun into TrainRuns * replaced settings::Dict with type Settings as struct * replaced path::Dict with type Path as struct * restructured examples/ and data/ in docs/ and test/ @@ -167,18 +168,18 @@ Modules and variables were renamed. Proof of concept and master thesis submission. -[Unreleased]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.8...main -[0.8]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.7...v0.8 -[0.7]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.6.2...v0.7 -[0.6.2]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.6.1...v0.6.2 -[0.6.1]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.6...v0.6.1 -[0.6]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.5.3...v0.6 -[0.5.3]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.5.2...v0.5.3 -[0.5.2]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.5.1...v0.5.2 -[0.5.1]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.5...v0.5.1 -[0.5]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.4.1...v0.5 -[0.4.1]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.4...v0.4.1 -[0.4]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.3...v0.4 -[0.3]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.2...v0.3 -[0.2]: https://github.com/railtoolkit/TrainRun.jl/compare/v0.1...v0.2 -[0.1]: https://github.com/railtoolkit/TrainRun.jl/releases/tag/v0.1 \ No newline at end of file +[Unreleased]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.8...main +[0.8]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.7...v0.8 +[0.7]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.6.2...v0.7 +[0.6.2]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.6.1...v0.6.2 +[0.6.1]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.6...v0.6.1 +[0.6]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.5.3...v0.6 +[0.5.3]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.5.2...v0.5.3 +[0.5.2]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.5.1...v0.5.2 +[0.5.1]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.5...v0.5.1 +[0.5]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.4.1...v0.5 +[0.4.1]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.4...v0.4.1 +[0.4]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.3...v0.4 +[0.3]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.2...v0.3 +[0.2]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.1...v0.2 +[0.1]: https://github.com/railtoolkit/TrainRuns.jl/releases/tag/v0.1 \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff index f2822e0..7b696cd 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ # Visit https://bit.ly/cffinit to generate yours today! --- cff-version: 1.2.0 -title: TrainRun.jl +title: TrainRuns.jl message: 'If you use this software, please cite it using these metadata.' type: software authors: @@ -22,12 +22,12 @@ identifiers: - type: doi value: 10.5281/zenodo.6448564 description: Current version -url: 'https://www.railtoolkit.org/projects/TrainRun.jl/' -repository: 'https://github.com/railtoolkit/TrainRun.jl' +url: 'https://www.railtoolkit.org/projects/TrainRuns.jl/' +repository: 'https://github.com/railtoolkit/TrainRuns.jl' abstract: >- - TrainRun.jl is a step towards open science and open data in railway engineering. + TrainRuns.jl is a step towards open science and open data in railway engineering. Its modular design offers the possibility to serve as a basis for future - optimization and development. TrainRun.jl is suitable for qualitative + optimization and development. TrainRuns.jl is suitable for qualitative calculations to compare different trains, and it is publicly available, and we invite others to collaborate. keywords: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf5bac4..dbc6ddb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ Please note we have a code of conduct, please follow it in all your interactions Link your local git repository to Julia: ```console -$ ln -s ~/path/to/TrainRun.jl ~/.julia/dev/TrainRun +$ ln -s ~/path/to/TrainRuns.jl ~/.julia/dev/TrainRuns ``` Have a look how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev @@ -19,22 +19,22 @@ Pkg.add("Revise") ``` and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file). -You can overide the standard TrainRun package with the local development branch (see linking above) with: +You can overide the standard TrainRuns package with the local development branch (see linking above) with: ```julia julia> # use the ] key -(@v1.x) pkg> develop TrainRun +(@v1.x) pkg> develop TrainRuns (@v1.x) pkg> # use backspace -julia> using TrainRun # local development branch will be loaded +julia> using TrainRuns # local development branch will be loaded ``` If you want to add a dependency use: ```julia julia> # use the ] key -(@v1.x) pkg> activate TrainRun -(TrainRun) pkg> +(@v1.x) pkg> activate TrainRuns +(TrainRuns) pkg> ``` -## TrainRun files +## TrainRuns files TODO! @@ -44,7 +44,7 @@ TODO! or related issues for context, before opening a new issue * Including minimal examples is greatly appreciated * If it's a bug, or unexpected behaviour, reproducing on the latest development version - (`Pkg.add(name="TrainRun", rev="main")`) is a good gut check and can streamline the process, + (`Pkg.add(name="TrainRuns", rev="main")`) is a good gut check and can streamline the process, along with including the first two lines of output from `versioninfo()` ## Style Guidelines @@ -54,12 +54,12 @@ TODO ## Git Recommendations For Pull Requests * Avoid working from the `main` branch of your fork, creating a new branch will make it - easier if TrainRun.jl `main` branch changes and you need to update your pull request; + easier if TrainRuns.jl `main` branch changes and you need to update your pull request; * All PRs and issues should be opened against the `main` branch not against the current release; * Run tests of your code before sending any commit to GitHub. Only push changes when the tests of the change are passing locally. In particular note that it is not a problem if you send several commits in one push command to GitHub as CI will be run only once then; -* If any conflicts arise due to changes in TrainRun.jl `main` branch, prefer updating your pull +* If any conflicts arise due to changes in TrainRuns.jl `main` branch, prefer updating your pull request branch with `git rebase` (rather than `git merge`), since the latter will introduce a merge commit that might confuse GitHub when displaying the diff of your PR, which makes your changes more difficult to review. Alternatively use conflict resolution tool available at GitHub; diff --git a/Project.toml b/Project.toml index a5fefaf..d09852c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,4 +1,4 @@ -name = "TrainRun" +name = "TrainRuns" uuid = "e4541106-d44c-4e00-b50b-ecdf479fcf92" authors = ["Max Kannenberg", "Martin Scheidt", "contributors"] version = "0.8.0" diff --git a/README.md b/README.md index de40d17..7663b08 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# TrainRun +# TrainRuns [![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=main)](https://github.com/railtoolkit/TrainRun.jl/actions/workflows/CI.yml?query=branch%3Amain) +[![Build Status](https://github.com/railtoolkit/TrainRuns.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/railtoolkit/TrainRuns.jl/actions/workflows/CI.yml?query=branch%3Amain) ------------ # About -TrainRun.jl is a step towards open science and open data in railway engineering. Its modular design offers the possibility to serve as a basis for future optimization and development. TrainRun.jl is suitable for qualitative calculations to compare different trains, and it is publicly available, and we invite others to collaborate. +TrainRuns.jl is a step towards open science and open data in railway engineering. Its modular design offers the possibility to serve as a basis for future optimization and development. TrainRuns.jl is suitable for qualitative calculations to compare different trains, and it is publicly available, and we invite others to collaborate. ------------ @@ -25,7 +25,7 @@ The required julia packages are # Minimal working example ```julia -import TrainRun +import TrainRuns train = Train("test/data/trains/freight.yaml") path = Path("test/data/paths/const.yaml") diff --git a/docs/Project.toml b/docs/Project.toml index b513025..59ca3f3 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,3 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -TrainRun = "e4541106-d44c-4e00-b50b-ecdf479fcf92" +TrainRuns = "e4541106-d44c-4e00-b50b-ecdf479fcf92" diff --git a/docs/examples/ExtendedWorkingExample.jl b/docs/examples/ExtendedWorkingExample.jl index f6a4123..1dadfc7 100644 --- a/docs/examples/ExtendedWorkingExample.jl +++ b/docs/examples/ExtendedWorkingExample.jl @@ -1,6 +1,6 @@ #!/usr/bin/env julia -import TrainRun +import TrainRuns paths=[] push!(paths, importFromYaml(:path, "data/paths/path_1_10km_nConst_vConst.yaml")) diff --git a/docs/examples/MinimalWorkingExample.jl b/docs/examples/MinimalWorkingExample.jl index d760abc..129bb2b 100644 --- a/docs/examples/MinimalWorkingExample.jl +++ b/docs/examples/MinimalWorkingExample.jl @@ -1,6 +1,6 @@ #!/usr/bin/env julia -using TrainRun +using TrainRuns train = Train("test/data/trains/freight.yaml") path = Path("test/data/paths/const.yaml") diff --git a/docs/make.jl b/docs/make.jl index f2bf563..c9fb66a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,16 +1,16 @@ -using TrainRun +using TrainRuns using Documenter -DocMeta.setdocmeta!(TrainRun, :DocTestSetup, :(using TrainRun); recursive=true) +DocMeta.setdocmeta!(TrainRuns, :DocTestSetup, :(using TrainRuns); recursive=true) makedocs(; - modules=[TrainRun], + modules=[TrainRuns], authors="Max Kannenberg, Martin Scheidt, and contributors", - repo="https://github.com/railtoolkit/TrainRun.jl/blob/{commit}{path}#{line}", - sitename="TrainRun.jl", + repo="https://github.com/railtoolkit/TrainRuns.jl/blob/{commit}{path}#{line}", + sitename="TrainRuns.jl", format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://railtoolkit.github.io/TrainRun.jl", + canonical="https://railtoolkit.github.io/TrainRuns.jl", assets=String[], ), pages=[ @@ -19,6 +19,6 @@ makedocs(; ) deploydocs(; - repo="github.com/railtoolkit/TrainRun.jl", + repo="github.com/railtoolkit/TrainRuns.jl", devbranch="development", ) diff --git a/docs/src/index.md b/docs/src/index.md index 217d810..4cd232d 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,14 +1,14 @@ ```@meta -CurrentModule = TrainRun +CurrentModule = TrainRuns ``` -# TrainRun +# TrainRuns -Documentation for [TrainRun](https://github.com/railtoolkit/TrainRun.jl). +Documentation for [TrainRuns](https://github.com/railtoolkit/TrainRuns.jl). ```@index ``` ```@autodocs -Modules = [TrainRun] +Modules = [TrainRuns] ``` diff --git a/src/TrainRun.jl b/src/TrainRuns.jl similarity index 98% rename from src/TrainRun.jl rename to src/TrainRuns.jl index aacaa89..eca0419 100644 --- a/src/TrainRun.jl +++ b/src/TrainRuns.jl @@ -6,7 +6,7 @@ # __license__ = "ISC" __precompile__(true) -module TrainRun +module TrainRuns ## loading standard library packages using UUIDs, Dates @@ -65,4 +65,4 @@ function trainrun(trainInput::Dict, path::Path, settings=Settings()::Settings) return output end # function trainrun -end # module TrainRun +end # module TrainRuns diff --git a/test/runtests.jl b/test/runtests.jl index a85821f..3476764 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,7 +5,7 @@ # __copyright__ = "2021" # __license__ = "ISC" -using TrainRun, Test +using TrainRuns, Test trains = Dict() paths = Dict() @@ -14,9 +14,9 @@ settings = Dict() @testset "load data" begin println("testing load train data") - push!(trains, :freight => @time TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml")) - push!(trains, :local => @time TrainRun.importFromYaml(:train, "test/data/trains/local.yaml")) - push!(trains, :longdistance => @time TrainRun.importFromYaml(:train, "test/data/trains/longdistance.yaml")) + push!(trains, :freight => @time TrainRuns.importFromYaml(:train, "test/data/trains/freight.yaml")) + push!(trains, :local => @time TrainRuns.importFromYaml(:train, "test/data/trains/local.yaml")) + push!(trains, :longdistance => @time TrainRuns.importFromYaml(:train, "test/data/trains/longdistance.yaml")) println("testing load path data") push!(paths, :const => @time Path("test/data/paths/const.yaml")) From 0b84ac0d9aa63d21ad7e000e51ee592d8a0e0495 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Wed, 4 May 2022 17:25:08 +0200 Subject: [PATCH 09/18] updated license and usage --- LICENSE | 2 +- README.md | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index f71894f..58a4eaf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License (ISC) -Copyright 2021 Max Kannenberg +Copyright 2022 Max Kannenberg, Martin scheidt Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice diff --git a/README.md b/README.md index 7663b08..56a7171 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # TrainRuns -[![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/TrainRuns.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/railtoolkit/TrainRuns.jl/actions/workflows/CI.yml?query=branch%3Amain) +[![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/TrainRuns.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/railtoolkit/TrainRuns.jl/actions/workflows/CI.yml?query=branch%3Amain) ------------ @@ -13,10 +12,17 @@ TrainRuns.jl is a step towards open science and open data in railway engineering # Installation +Use the package manager provided by julia: +```julia +julia> # use the ] key +(@v1.x) pkg> add TrainRuns +(@v1.x) pkg> # use backspace +julia> using TrainRuns +``` + The required julia packages are - YAML.jl - JSONSchema.jl - - Dates.jl - DataFrames.jl - CSV.jl @@ -25,7 +31,7 @@ The required julia packages are # Minimal working example ```julia -import TrainRuns +using TrainRuns train = Train("test/data/trains/freight.yaml") path = Path("test/data/paths/const.yaml") @@ -45,11 +51,11 @@ This work was supervised by South Westphalia University of Applied Sciences and # License - [![Open Source Initiative Approved License logo](https://opensource.org/files/OSIApproved_100X125.png "Open Source Initiative Approved License logo")](https://opensource.org) +[![Open Source Initiative Approved License logo](https://opensource.org/files/OSIApproved_100X125.png "Open Source Initiative Approved License logo")](https://opensource.org) ISC License (ISC) -Copyright 2021 Max Kannenberg +Copyright 2022 Max Kannenberg, Martin Scheidt Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. From a752987b34ac90a324872c8b439a3314c4d35c4e Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Fri, 6 May 2022 00:05:48 +0200 Subject: [PATCH 10/18] added mail address --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 561c180..d14ad9e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +reported by contacting the project team at railtoolkit@ownx.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From f10d20b0b1a33448f11a18c1fda0e9275749a09b Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Fri, 6 May 2022 00:26:25 +0200 Subject: [PATCH 11/18] Updated path schema to 2022.05 --- src/types.jl | 141 ++++--- test/data/paths/const.yaml | 36 +- test/data/paths/realworld.yaml | 718 +++++++++++++++++---------------- test/data/paths/slope.yaml | 52 +-- test/data/paths/speed.yaml | 52 +-- 5 files changed, 526 insertions(+), 473 deletions(-) diff --git a/src/types.jl b/src/types.jl index 8f9de9b..b457958 100644 --- a/src/types.jl +++ b/src/types.jl @@ -110,6 +110,7 @@ end #struct Settings Path is a datastruture for calculation context. The function Path() will create a running path for the train. +Supported formats are: railtoolkit/schema (2022.05) # Example ```jldoctest @@ -141,68 +142,112 @@ struct Path ## load from file if type == :YAML - path = YAML.load(open(file))["path"] + data = YAML.load(open(file)) + if data["schema"] != "https://railtoolkit.org/schema/running-path.json" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/running-path (2022.05)") + end + if data["schema_schema"] != "2022.05" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/running-path (2022.05)") + end ## JSON schema for YAML-file validation railtoolkit_schema = Schema("""{ - "required": [ "name", "id", "characteristic_sections" ], + "required": [ "schema", "schema_version", "paths" ], "properties": { - "characteristic_sections": { - "description": "", - "type": "array", - "minItems": 2, - "uniqueItems": true, - "items": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "description": "", - "prefixItems": [ - { "type": "number" }, - { "type": "number" }, - { "type": "number" } - ] - } - }, - "id": { - "description": "Identifier of the path", - "type": "string" - }, - "name": { - "description": "Name of the path", - "type": "string" - }, - "points_of_interest": { - "description": "", - "type": "array", - "uniqueItems": true, - "items": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "description": "", - "prefixItems": [ - { "type": "number" }, - { "type": "string" }, - { "enum": [ "front", "rear" ] } - ] - } - }, - "UUID": { - "description": "The unique identifier for a path", + "schema": { + "description": "Identifier of the schema", + "enum": [ "https://railtoolkit.org/schema/running-path.json" ] + }, + "schema_version": { + "description": "Version of the schema", "type": "string", - "format": "uuid" + "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" + }, + "paths": { + "type": "array", + "minItems": 1, + "items": { + "required": [ "name", "id", "characteristic_sections" ], + "type": "object", + "properties": { + "characteristic_sections": { + "description": "", + "type": "array", + "minItems": 2, + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { + "description": "milage in meter", + "type": "number" + }, + { + "description": "speed in kilometers per hour", + "type": "number", + "exclusiveMinimum": 0 + }, + { + "description": "resistance in permil", + "type": "number" + } + ] + } + }, + "id": { + "description": "Identifier of the path", + "type": "string" + }, + "name": { + "description": "Name of the path", + "type": "string" + }, + "points_of_interest": { + "description": "", + "type": "array", + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { "type": "number" }, + { "type": "string" }, + { "enum": [ "front", "rear" ] } + ] + } + }, + "UUID": { + "description": "The unique identifier for a path", + "type": "string", + "format": "uuid" + } + } } + } } }""") + paths = data["paths"] try - validate(railtoolkit_schema, path) + validate(railtoolkit_schema, paths) catch err error("Could not load path file '$file'.\n YAML format is not recognized. - Currently supported: railtoolkit/schema (2022.04)") + Currently supported: railtoolkit/schema/running-path (2022.05)") end + if length(paths) > 1 + println("WARNING: the loaded file contains more than one path. Using only the first!") + end + path = paths[1] ## set the variables if they exist in "settings" # required diff --git a/test/data/paths/const.yaml b/test/data/paths/const.yaml index 94ec59e..9511d70 100644 --- a/test/data/paths/const.yaml +++ b/test/data/paths/const.yaml @@ -1,19 +1,21 @@ %YAML 1.2 --- -path: - name: "10 km, no gradient, 160 km/h" - id: const - UUID: 23ff336e-9b9a-4535-bdb6-9db488b10945 - points_of_interest: - # [ station in m, name, front or rear ] - - [ 999.00, point_1, front ] - - [ 2000.00, point_2, front ] - - [ 3333.30, point_3, rear ] - - [ 5000.00, point_4, front ] - - [ 7777.00, point_5, front ] - - [ 9000.00, point_6, front ] - - [ 9500.95, point_7, front ] - characteristic_sections: - # [ station in m, speed limit in km/h, resistance in ‰ ] - - [ 0.0, 160, 0.00 ] - - [ 10000.0, 160, 0.00 ] +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2022.05" +paths: + - name: "10 km, no gradient, 160 km/h" + id: const + UUID: 23ff336e-9b9a-4535-bdb6-9db488b10945 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 999.00, point_1, front ] + - [ 2000.00, point_2, front ] + - [ 3333.30, point_3, rear ] + - [ 5000.00, point_4, front ] + - [ 7777.00, point_5, front ] + - [ 9000.00, point_6, front ] + - [ 9500.95, point_7, front ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 10000.0, 160, 0.00 ] diff --git a/test/data/paths/realworld.yaml b/test/data/paths/realworld.yaml index f8d7925..e19b325 100644 --- a/test/data/paths/realworld.yaml +++ b/test/data/paths/realworld.yaml @@ -1,360 +1,362 @@ %YAML 1.2 --- -path: - name: "'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5" - id: realworld - UUID: 2b31a0c5-85bc-4721-b7e0-66f9df95f7b6 - # source: https://www.railml.org/en/user/exampledata.html - # -> "Real world railway examples from professional tools" - # -> "East Saxony railway network by FBS" - # -> "Ostsachsen_V220.railml" - # -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5 - characteristic_sections: - # [ s in m, v_limit in km/h, f_Rp in ‰ ] - - [ 0.0, 40, 0.0 ] - - [ 318.0, 40, 2.0 ] - - [ 399.0, 40, -3.0 ] - - [ 500.0, 40, 0.0 ] - - [ 579.0, 40, 1.0 ] - - [ 784.0, 40, 5.3 ] - - [ 868.0, 40, 20.0 ] - - [ 1082.0, 40, 16.1 ] - - [ 1287.0, 40, 18.1 ] - - [ 1800.0, 110, 18.1 ] - - [ 2242.0, 110, 15.4 ] - - [ 3295.0, 110, 11.0 ] - - [ 3880.0, 110, 11.1 ] - - [ 4680.0, 45, 11.1 ] - - [ 4686.0, 90, 11.1 ] - - [ 6122.0, 90, 0.0 ] - - [ 6487.0, 90, 1.5 ] - - [ 6588.0, 70, 1.5 ] - - [ 6589.0, 70, 2.4 ] - - [ 6608.0, 130, 2.4 ] - - [ 6723.0, 150, 1.3 ] - - [ 6928.0, 160, 1.3 ] - - [ 7030.0, 160, 7.5 ] - - [ 7300.0, 160, 8.0 ] - - [ 7552.0, 160, 5.0 ] - - [ 7675.0, 160, 6.7 ] - - [ 7800.0, 160, 7.4 ] - - [ 7920.0, 160, 7.0 ] - - [ 8020.0, 140, 7.0 ] - - [ 8100.0, 140, 6.3 ] - - [ 8168.0, 140, 7.4 ] - - [ 8226.0, 140, 8.4 ] - - [ 8300.0, 140, 7.4 ] - - [ 8381.0, 150, 7.4 ] - - [ 8600.0, 150, 7.8 ] - - [ 8900.0, 150, 6.7 ] - - [ 9100.0, 150, 7.8 ] - - [ 9600.0, 150, 8.0 ] - - [ 9845.0, 150, 7.3 ] - - [ 10005.0, 160, 7.3 ] - - [ 10600.0, 160, 6.0 ] - - [ 10748.0, 160, 7.3 ] - - [ 11100.0, 160, 4.5 ] - - [ 11280.0, 160, 3.6 ] - - [ 11450.0, 160, 7.2 ] - - [ 11800.0, 160, 6.8 ] - - [ 12100.0, 160, 7.3 ] - - [ 12590.0, 160, 7.6 ] - - [ 13100.0, 160, 7.3 ] - - [ 13500.0, 160, 7.1 ] - - [ 13800.0, 160, 7.3 ] - - [ 14138.0, 150, 7.3 ] - - [ 14330.0, 150, 0.3 ] - - [ 14640.0, 150, -1.8 ] - - [ 14764.0, 160, -2.8 ] - - [ 15000.0, 160, -3.3 ] - - [ 15500.0, 160, -0.9 ] - - [ 16000.0, 160, 0.0 ] - - [ 16470.0, 160, 1.2 ] - - [ 16572.0, 160, 2.2 ] - - [ 16700.0, 160, 3.8 ] - - [ 16949.0, 160, 3.5 ] - - [ 17086.0, 160, 3.2 ] - - [ 17232.0, 160, 1.3 ] - - [ 17339.0, 160, 4.5 ] - - [ 17406.0, 160, 3.4 ] - - [ 17727.0, 150, 3.4 ] - - [ 17807.0, 150, 4.6 ] - - [ 18049.0, 150, 3.0 ] - - [ 18210.0, 140, 3.0 ] - - [ 18300.0, 140, 4.6 ] - - [ 18680.0, 140, 3.2 ] - - [ 18761.0, 150, 3.2 ] - - [ 19047.0, 150, 5.1 ] - - [ 19305.0, 150, 3.0 ] - - [ 19406.0, 160, 3.0 ] - - [ 19414.0, 160, -0.3 ] - - [ 19900.0, 160, 3.0 ] - - [ 20150.0, 160, 1.8 ] - - [ 20470.0, 160, -4.0 ] - - [ 20940.0, 160, -3.6 ] - - [ 21150.0, 160, -1.5 ] - - [ 21390.0, 160, 0.0 ] - - [ 21702.0, 160, 1.5 ] - - [ 22188.0, 150, 1.5 ] - - [ 22294.0, 150, 1.8 ] - - [ 22383.0, 160, 1.8 ] - - [ 22500.0, 160, 2.6 ] - - [ 22900.0, 160, 4.6 ] - - [ 23542.0, 160, 0.2 ] - - [ 23736.0, 160, 0.9 ] - - [ 24124.0, 160, 7.0 ] - - [ 24918.0, 160, 7.6 ] - - [ 25100.0, 150, 7.1 ] - - [ 25580.0, 150, 7.4 ] - - [ 25708.0, 160, 7.4 ] - - [ 25810.0, 160, 7.1 ] - - [ 26040.0, 160, 3.2 ] - - [ 26330.0, 160, 1.8 ] - - [ 26593.0, 160, 2.1 ] - - [ 27020.0, 160, 3.5 ] - - [ 27195.0, 160, 5.8 ] - - [ 27253.0, 160, 1.1 ] - - [ 27310.0, 160, 3.5 ] - - [ 27595.0, 160, 3.4 ] - - [ 28530.0, 160, 4.6 ] - - [ 29115.0, 160, 0.0 ] - - [ 29700.0, 160, -5.2 ] - - [ 30055.0, 120, -4.3 ] - - [ 30301.0, 120, -6.2 ] - - [ 30487.0, 160, -6.2 ] - - [ 30537.0, 160, -4.8 ] - - [ 31130.0, 160, -0.3 ] - - [ 31293.0, 160, 3.7 ] - - [ 31400.0, 160, 2.9 ] - - [ 31640.0, 160, 4.1 ] - - [ 31795.0, 120, 4.1 ] - - [ 32010.0, 120, 3.2 ] - - [ 32138.0, 130, 3.2 ] - - [ 32365.0, 130, -4.0 ] - - [ 33000.0, 130, 6.1 ] - - [ 33426.0, 160, 6.1 ] - - [ 33907.0, 160, 7.1 ] - - [ 34220.0, 160, 8.4 ] - - [ 34300.0, 160, 7.9 ] - - [ 34440.0, 160, 7.1 ] - - [ 34610.0, 160, 4.4 ] - - [ 35000.0, 160, 5.6 ] - - [ 35173.0, 150, 5.6 ] - - [ 35400.0, 150, -0.2 ] - - [ 35597.0, 160, -0.2 ] - - [ 35900.0, 160, 0.0 ] - - [ 36700.0, 160, 3.9 ] - - [ 36938.0, 160, 0.0 ] - - [ 37700.0, 160, -0.3 ] - - [ 37978.0, 150, -5.6 ] - - [ 38063.0, 150, -1.9 ] - - [ 38141.0, 150, -3.1 ] - - [ 38210.0, 150, 0.0 ] - - [ 38406.0, 150, -7.0 ] - - [ 38900.0, 150, -7.5 ] - - [ 39200.0, 150, -8.7 ] - - [ 39298.0, 150, -6.4 ] - - [ 39476.0, 150, -7.2 ] - - [ 40400.0, 150, -6.9 ] - - [ 40676.0, 130, -6.9 ] - - [ 41000.0, 130, -7.3 ] - - [ 41406.0, 130, -7.5 ] - - [ 41571.0, 160, -7.3 ] - - [ 41816.0, 160, -6.8 ] - - [ 41923.0, 160, -7.1 ] - - [ 42139.0, 160, -1.3 ] - - [ 42343.0, 160, -2.4 ] - - [ 42432.0, 150, -2.4 ] - - [ 42952.0, 160, -2.4 ] - - [ 43000.0, 160, -4.6 ] - - [ 43264.0, 160, -2.5 ] - - [ 43388.0, 160, -0.6 ] - - [ 43700.0, 160, 0.8 ] - - [ 44030.0, 160, 2.7 ] - - [ 44430.0, 160, 0.0 ] - - [ 44708.0, 160, -7.2 ] - - [ 45477.0, 160, -7.8 ] - - [ 45890.0, 160, -1.0 ] - - [ 46562.0, 160, -1.6 ] - - [ 47000.0, 160, -6.9 ] - - [ 47500.0, 160, -7.7 ] - - [ 47800.0, 160, -6.8 ] - - [ 48700.0, 160, -4.3 ] - - [ 49218.0, 160, -5.3 ] - - [ 49514.0, 160, -2.5 ] - - [ 50000.0, 160, -2.2 ] - - [ 51150.0, 160, -5.7 ] - - [ 51406.0, 160, -6.3 ] - - [ 51710.0, 150, -5.8 ] - - [ 52000.0, 150, -6.7 ] - - [ 52215.0, 150, -6.1 ] - - [ 53213.0, 150, -6.8 ] - - [ 53567.0, 150, -5.6 ] - - [ 53943.0, 150, -6.5 ] - - [ 54129.0, 140, -6.5 ] - - [ 54212.0, 140, -10.5 ] - - [ 54247.0, 140, -5.4 ] - - [ 54326.0, 140, -6.1 ] - - [ 54450.0, 140, -6.5 ] - - [ 54482.0, 120, -6.5 ] - - [ 54550.0, 120, -6.2 ] - - [ 54855.0, 140, -6.2 ] - - [ 55307.0, 140, -0.3 ] - - [ 55651.0, 140, -1.2 ] - - [ 55788.0, 140, 0.0 ] - - [ 55918.0, 100, 0.0 ] - - [ 56433.0, 150, 0.0 ] - - [ 56560.0, 150, -2.1 ] - - [ 56624.0, 150, -6.7 ] - - [ 57012.0, 150, 1.3 ] - - [ 57260.0, 150, 6.6 ] - - [ 57800.0, 150, 5.3 ] - - [ 57987.0, 150, 0.0 ] - - [ 57990.0, 150, 7.6 ] - - [ 58321.0, 150, 6.4 ] - - [ 59090.0, 150, 6.9 ] - - [ 59468.0, 150, 9.2 ] - - [ 59600.0, 150, 0.2 ] - - [ 60683.0, 150, 4.1 ] - - [ 61156.0, 150, 2.3 ] - - [ 61181.0, 130, 2.3 ] - - [ 61325.0, 130, 0.0 ] - - [ 61605.0, 130, 7.3 ] - - [ 62108.0, 150, 7.3 ] - - [ 62246.0, 150, 6.6 ] - - [ 62279.0, 150, 5.1 ] - - [ 62454.0, 150, 0.9 ] - - [ 62777.0, 150, 5.5 ] - - [ 63802.0, 150, 4.6 ] - - [ 64344.0, 150, 0.2 ] - - [ 64932.0, 150, -0.6 ] - - [ 65100.0, 150, 0.0 ] - - [ 65690.0, 150, 1.8 ] - - [ 65878.0, 150, 2.5 ] - - [ 66266.0, 150, -1.0 ] - - [ 66339.0, 150, 6.3 ] - - [ 66448.0, 160, 6.3 ] - - [ 66587.0, 160, 0.0 ] - - [ 66856.0, 160, 3.2 ] - - [ 67480.0, 160, 3.6 ] - - [ 67697.0, 160, 2.2 ] - - [ 67800.0, 160, 6.0 ] - - [ 67851.0, 130, 6.0 ] - - [ 68027.0, 130, 2.7 ] - - [ 68172.0, 130, 0.6 ] - - [ 68328.0, 130, 2.5 ] - - [ 68357.0, 130, 0.0 ] - - [ 68479.0, 130, 7.1 ] - - [ 68783.0, 130, 7.4 ] - - [ 69056.0, 150, 7.4 ] - - [ 69500.0, 150, 6.8 ] - - [ 69741.0, 160, 6.8 ] - - [ 69900.0, 160, 6.6 ] - - [ 70757.0, 160, 3.6 ] - - [ 71384.0, 160, 6.0 ] - - [ 71568.0, 160, 7.1 ] - - [ 71800.0, 160, 7.4 ] - - [ 72100.0, 160, 7.3 ] - - [ 73919.0, 150, 7.3 ] - - [ 74317.0, 140, 7.3 ] - - [ 74448.0, 140, 1.5 ] - - [ 74590.0, 140, -1.5 ] - - [ 74620.0, 140, -5.0 ] - - [ 74950.0, 140, -4.3 ] - - [ 75100.0, 140, -1.8 ] - - [ 75154.0, 130, -1.8 ] - - [ 75260.0, 130, -6.2 ] - - [ 75873.0, 130, -5.6 ] - - [ 76062.0, 100, -5.6 ] - - [ 76100.0, 100, -6.4 ] - - [ 76350.0, 100, -5.7 ] - - [ 76476.0, 100, -7.0 ] - - [ 76600.0, 100, -6.4 ] - - [ 76601.0, 90, -6.4 ] - - [ 76726.0, 90, -6.2 ] - - [ 77256.0, 90, -2.1 ] - - [ 77285.0, 80, -2.1 ] - - [ 77299.0, 80, -14.0 ] - - [ 77331.0, 80, -1.6 ] - - [ 77379.0, 90, -1.6 ] - - [ 77425.0, 110, -1.6 ] - - [ 77455.0, 110, -4.9 ] - - [ 77498.0, 110, 0.0 ] - - [ 77505.0, 160, 0.0 ] - - [ 77555.0, 160, -5.6 ] - - [ 77662.0, 160, 0.0 ] - - [ 78085.0, 160, -4.0 ] - - [ 78223.0, 160, -6.8 ] - - [ 78337.0, 130, -6.8 ] - - [ 78856.0, 130, -6.6 ] - - [ 78875.0, 130, -7.2 ] - - [ 79345.0, 150, -7.2 ] - - [ 79600.0, 150, -6.5 ] - - [ 79792.0, 150, -0.2 ] - - [ 80537.0, 150, -5.2 ] - - [ 81300.0, 150, -4.8 ] - - [ 81634.0, 110, -4.8 ] - - [ 81943.0, 110, -5.4 ] - - [ 82166.0, 110, 0.0 ] - - [ 82408.0, 110, 4.8 ] - - [ 82790.0, 110, 5.9 ] - - [ 83137.0, 120, 5.9 ] - - [ 83300.0, 120, 0.0 ] - - [ 83519.0, 150, 0.0 ] - - [ 83597.0, 150, -7.8 ] - - [ 83827.0, 150, -8.1 ] - - [ 84150.0, 150, -7.2 ] - - [ 84391.0, 150, 0.0 ] - - [ 84966.0, 150, -4.0 ] - - [ 85529.0, 130, -2.3 ] - - [ 85589.0, 130, -4.0 ] - - [ 86081.0, 130, 0.0 ] - - [ 86514.0, 130, 7.8 ] - - [ 86577.0, 120, 7.8 ] - - [ 87554.0, 90, 7.8 ] - - [ 87690.0, 90, 7.9 ] - - [ 87842.0, 90, 1.7 ] - - [ 88007.0, 110, 1.7 ] - - [ 88100.0, 110, 3.4 ] - - [ 88260.0, 110, 5.3 ] - - [ 88376.0, 160, 5.3 ] - - [ 88450.0, 160, 6.7 ] - - [ 89050.0, 160, 7.4 ] - - [ 89350.0, 160, -7.1 ] - - [ 90365.0, 160, 0.0 ] - - [ 90700.0, 160, -7.3 ] - - [ 92000.0, 160, -7.9 ] - - [ 92166.0, 160, -4.0 ] - - [ 92460.0, 160, -5.4 ] - - [ 93330.0, 160, 0.0 ] - - [ 93901.0, 160, 0.7 ] - - [ 94156.0, 160, 5.2 ] - - [ 94440.0, 160, -2.8 ] - - [ 94530.0, 160, -0.8 ] - - [ 94630.0, 160, -6.8 ] - - [ 94830.0, 160, -4.4 ] - - [ 95090.0, 160, -4.6 ] - - [ 95500.0, 160, -4.8 ] - - [ 96500.0, 160, -4.4 ] - - [ 96700.0, 160, -5.6 ] - - [ 97000.0, 160, -4.6 ] - - [ 97590.0, 160, 0.0 ] - - [ 97858.0, 120, 0.0 ] - - [ 98224.0, 120, -1.5 ] - - [ 98264.0, 120, 0.0 ] - - [ 98577.0, 120, 7.5 ] - - [ 98738.0, 120, 0.0 ] - - [ 99055.0, 130, 0.0 ] - - [ 99427.0, 130, -2.0 ] - - [ 99610.0, 130, -3.1 ] - - [ 99906.0, 120, -3.1 ] - - [ 99980.0, 120, -1.3 ] - - [100190.0, 120, -6.8 ] - - [100832.0, 120, -7.2 ] - - [100980.0, 120, -8.1 ] - - [101100.0, 120, -7.4 ] - - [101245.0, 120, -6.3 ] - - [101332.0, 100, -6.3 ] - - [101365.0, 100, -2.4 ] - - [101551.0, 110, -2.4 ] - - [101800.0, 110, 0.0 ] +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2022.05" +paths: + - name: "'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5" + id: realworld + UUID: 2b31a0c5-85bc-4721-b7e0-66f9df95f7b6 + # source: https://www.railml.org/en/user/exampledata.html + # -> "Real world railway examples from professional tools" + # -> "East Saxony railway network by FBS" + # -> "Ostsachsen_V220.railml" + # -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5 + characteristic_sections: + # [ s in m, v_limit in km/h, f_Rp in ‰ ] + - [ 0.0, 40, 0.0 ] + - [ 318.0, 40, 2.0 ] + - [ 399.0, 40, -3.0 ] + - [ 500.0, 40, 0.0 ] + - [ 579.0, 40, 1.0 ] + - [ 784.0, 40, 5.3 ] + - [ 868.0, 40, 20.0 ] + - [ 1082.0, 40, 16.1 ] + - [ 1287.0, 40, 18.1 ] + - [ 1800.0, 110, 18.1 ] + - [ 2242.0, 110, 15.4 ] + - [ 3295.0, 110, 11.0 ] + - [ 3880.0, 110, 11.1 ] + - [ 4680.0, 45, 11.1 ] + - [ 4686.0, 90, 11.1 ] + - [ 6122.0, 90, 0.0 ] + - [ 6487.0, 90, 1.5 ] + - [ 6588.0, 70, 1.5 ] + - [ 6589.0, 70, 2.4 ] + - [ 6608.0, 130, 2.4 ] + - [ 6723.0, 150, 1.3 ] + - [ 6928.0, 160, 1.3 ] + - [ 7030.0, 160, 7.5 ] + - [ 7300.0, 160, 8.0 ] + - [ 7552.0, 160, 5.0 ] + - [ 7675.0, 160, 6.7 ] + - [ 7800.0, 160, 7.4 ] + - [ 7920.0, 160, 7.0 ] + - [ 8020.0, 140, 7.0 ] + - [ 8100.0, 140, 6.3 ] + - [ 8168.0, 140, 7.4 ] + - [ 8226.0, 140, 8.4 ] + - [ 8300.0, 140, 7.4 ] + - [ 8381.0, 150, 7.4 ] + - [ 8600.0, 150, 7.8 ] + - [ 8900.0, 150, 6.7 ] + - [ 9100.0, 150, 7.8 ] + - [ 9600.0, 150, 8.0 ] + - [ 9845.0, 150, 7.3 ] + - [ 10005.0, 160, 7.3 ] + - [ 10600.0, 160, 6.0 ] + - [ 10748.0, 160, 7.3 ] + - [ 11100.0, 160, 4.5 ] + - [ 11280.0, 160, 3.6 ] + - [ 11450.0, 160, 7.2 ] + - [ 11800.0, 160, 6.8 ] + - [ 12100.0, 160, 7.3 ] + - [ 12590.0, 160, 7.6 ] + - [ 13100.0, 160, 7.3 ] + - [ 13500.0, 160, 7.1 ] + - [ 13800.0, 160, 7.3 ] + - [ 14138.0, 150, 7.3 ] + - [ 14330.0, 150, 0.3 ] + - [ 14640.0, 150, -1.8 ] + - [ 14764.0, 160, -2.8 ] + - [ 15000.0, 160, -3.3 ] + - [ 15500.0, 160, -0.9 ] + - [ 16000.0, 160, 0.0 ] + - [ 16470.0, 160, 1.2 ] + - [ 16572.0, 160, 2.2 ] + - [ 16700.0, 160, 3.8 ] + - [ 16949.0, 160, 3.5 ] + - [ 17086.0, 160, 3.2 ] + - [ 17232.0, 160, 1.3 ] + - [ 17339.0, 160, 4.5 ] + - [ 17406.0, 160, 3.4 ] + - [ 17727.0, 150, 3.4 ] + - [ 17807.0, 150, 4.6 ] + - [ 18049.0, 150, 3.0 ] + - [ 18210.0, 140, 3.0 ] + - [ 18300.0, 140, 4.6 ] + - [ 18680.0, 140, 3.2 ] + - [ 18761.0, 150, 3.2 ] + - [ 19047.0, 150, 5.1 ] + - [ 19305.0, 150, 3.0 ] + - [ 19406.0, 160, 3.0 ] + - [ 19414.0, 160, -0.3 ] + - [ 19900.0, 160, 3.0 ] + - [ 20150.0, 160, 1.8 ] + - [ 20470.0, 160, -4.0 ] + - [ 20940.0, 160, -3.6 ] + - [ 21150.0, 160, -1.5 ] + - [ 21390.0, 160, 0.0 ] + - [ 21702.0, 160, 1.5 ] + - [ 22188.0, 150, 1.5 ] + - [ 22294.0, 150, 1.8 ] + - [ 22383.0, 160, 1.8 ] + - [ 22500.0, 160, 2.6 ] + - [ 22900.0, 160, 4.6 ] + - [ 23542.0, 160, 0.2 ] + - [ 23736.0, 160, 0.9 ] + - [ 24124.0, 160, 7.0 ] + - [ 24918.0, 160, 7.6 ] + - [ 25100.0, 150, 7.1 ] + - [ 25580.0, 150, 7.4 ] + - [ 25708.0, 160, 7.4 ] + - [ 25810.0, 160, 7.1 ] + - [ 26040.0, 160, 3.2 ] + - [ 26330.0, 160, 1.8 ] + - [ 26593.0, 160, 2.1 ] + - [ 27020.0, 160, 3.5 ] + - [ 27195.0, 160, 5.8 ] + - [ 27253.0, 160, 1.1 ] + - [ 27310.0, 160, 3.5 ] + - [ 27595.0, 160, 3.4 ] + - [ 28530.0, 160, 4.6 ] + - [ 29115.0, 160, 0.0 ] + - [ 29700.0, 160, -5.2 ] + - [ 30055.0, 120, -4.3 ] + - [ 30301.0, 120, -6.2 ] + - [ 30487.0, 160, -6.2 ] + - [ 30537.0, 160, -4.8 ] + - [ 31130.0, 160, -0.3 ] + - [ 31293.0, 160, 3.7 ] + - [ 31400.0, 160, 2.9 ] + - [ 31640.0, 160, 4.1 ] + - [ 31795.0, 120, 4.1 ] + - [ 32010.0, 120, 3.2 ] + - [ 32138.0, 130, 3.2 ] + - [ 32365.0, 130, -4.0 ] + - [ 33000.0, 130, 6.1 ] + - [ 33426.0, 160, 6.1 ] + - [ 33907.0, 160, 7.1 ] + - [ 34220.0, 160, 8.4 ] + - [ 34300.0, 160, 7.9 ] + - [ 34440.0, 160, 7.1 ] + - [ 34610.0, 160, 4.4 ] + - [ 35000.0, 160, 5.6 ] + - [ 35173.0, 150, 5.6 ] + - [ 35400.0, 150, -0.2 ] + - [ 35597.0, 160, -0.2 ] + - [ 35900.0, 160, 0.0 ] + - [ 36700.0, 160, 3.9 ] + - [ 36938.0, 160, 0.0 ] + - [ 37700.0, 160, -0.3 ] + - [ 37978.0, 150, -5.6 ] + - [ 38063.0, 150, -1.9 ] + - [ 38141.0, 150, -3.1 ] + - [ 38210.0, 150, 0.0 ] + - [ 38406.0, 150, -7.0 ] + - [ 38900.0, 150, -7.5 ] + - [ 39200.0, 150, -8.7 ] + - [ 39298.0, 150, -6.4 ] + - [ 39476.0, 150, -7.2 ] + - [ 40400.0, 150, -6.9 ] + - [ 40676.0, 130, -6.9 ] + - [ 41000.0, 130, -7.3 ] + - [ 41406.0, 130, -7.5 ] + - [ 41571.0, 160, -7.3 ] + - [ 41816.0, 160, -6.8 ] + - [ 41923.0, 160, -7.1 ] + - [ 42139.0, 160, -1.3 ] + - [ 42343.0, 160, -2.4 ] + - [ 42432.0, 150, -2.4 ] + - [ 42952.0, 160, -2.4 ] + - [ 43000.0, 160, -4.6 ] + - [ 43264.0, 160, -2.5 ] + - [ 43388.0, 160, -0.6 ] + - [ 43700.0, 160, 0.8 ] + - [ 44030.0, 160, 2.7 ] + - [ 44430.0, 160, 0.0 ] + - [ 44708.0, 160, -7.2 ] + - [ 45477.0, 160, -7.8 ] + - [ 45890.0, 160, -1.0 ] + - [ 46562.0, 160, -1.6 ] + - [ 47000.0, 160, -6.9 ] + - [ 47500.0, 160, -7.7 ] + - [ 47800.0, 160, -6.8 ] + - [ 48700.0, 160, -4.3 ] + - [ 49218.0, 160, -5.3 ] + - [ 49514.0, 160, -2.5 ] + - [ 50000.0, 160, -2.2 ] + - [ 51150.0, 160, -5.7 ] + - [ 51406.0, 160, -6.3 ] + - [ 51710.0, 150, -5.8 ] + - [ 52000.0, 150, -6.7 ] + - [ 52215.0, 150, -6.1 ] + - [ 53213.0, 150, -6.8 ] + - [ 53567.0, 150, -5.6 ] + - [ 53943.0, 150, -6.5 ] + - [ 54129.0, 140, -6.5 ] + - [ 54212.0, 140, -10.5 ] + - [ 54247.0, 140, -5.4 ] + - [ 54326.0, 140, -6.1 ] + - [ 54450.0, 140, -6.5 ] + - [ 54482.0, 120, -6.5 ] + - [ 54550.0, 120, -6.2 ] + - [ 54855.0, 140, -6.2 ] + - [ 55307.0, 140, -0.3 ] + - [ 55651.0, 140, -1.2 ] + - [ 55788.0, 140, 0.0 ] + - [ 55918.0, 100, 0.0 ] + - [ 56433.0, 150, 0.0 ] + - [ 56560.0, 150, -2.1 ] + - [ 56624.0, 150, -6.7 ] + - [ 57012.0, 150, 1.3 ] + - [ 57260.0, 150, 6.6 ] + - [ 57800.0, 150, 5.3 ] + - [ 57987.0, 150, 0.0 ] + - [ 57990.0, 150, 7.6 ] + - [ 58321.0, 150, 6.4 ] + - [ 59090.0, 150, 6.9 ] + - [ 59468.0, 150, 9.2 ] + - [ 59600.0, 150, 0.2 ] + - [ 60683.0, 150, 4.1 ] + - [ 61156.0, 150, 2.3 ] + - [ 61181.0, 130, 2.3 ] + - [ 61325.0, 130, 0.0 ] + - [ 61605.0, 130, 7.3 ] + - [ 62108.0, 150, 7.3 ] + - [ 62246.0, 150, 6.6 ] + - [ 62279.0, 150, 5.1 ] + - [ 62454.0, 150, 0.9 ] + - [ 62777.0, 150, 5.5 ] + - [ 63802.0, 150, 4.6 ] + - [ 64344.0, 150, 0.2 ] + - [ 64932.0, 150, -0.6 ] + - [ 65100.0, 150, 0.0 ] + - [ 65690.0, 150, 1.8 ] + - [ 65878.0, 150, 2.5 ] + - [ 66266.0, 150, -1.0 ] + - [ 66339.0, 150, 6.3 ] + - [ 66448.0, 160, 6.3 ] + - [ 66587.0, 160, 0.0 ] + - [ 66856.0, 160, 3.2 ] + - [ 67480.0, 160, 3.6 ] + - [ 67697.0, 160, 2.2 ] + - [ 67800.0, 160, 6.0 ] + - [ 67851.0, 130, 6.0 ] + - [ 68027.0, 130, 2.7 ] + - [ 68172.0, 130, 0.6 ] + - [ 68328.0, 130, 2.5 ] + - [ 68357.0, 130, 0.0 ] + - [ 68479.0, 130, 7.1 ] + - [ 68783.0, 130, 7.4 ] + - [ 69056.0, 150, 7.4 ] + - [ 69500.0, 150, 6.8 ] + - [ 69741.0, 160, 6.8 ] + - [ 69900.0, 160, 6.6 ] + - [ 70757.0, 160, 3.6 ] + - [ 71384.0, 160, 6.0 ] + - [ 71568.0, 160, 7.1 ] + - [ 71800.0, 160, 7.4 ] + - [ 72100.0, 160, 7.3 ] + - [ 73919.0, 150, 7.3 ] + - [ 74317.0, 140, 7.3 ] + - [ 74448.0, 140, 1.5 ] + - [ 74590.0, 140, -1.5 ] + - [ 74620.0, 140, -5.0 ] + - [ 74950.0, 140, -4.3 ] + - [ 75100.0, 140, -1.8 ] + - [ 75154.0, 130, -1.8 ] + - [ 75260.0, 130, -6.2 ] + - [ 75873.0, 130, -5.6 ] + - [ 76062.0, 100, -5.6 ] + - [ 76100.0, 100, -6.4 ] + - [ 76350.0, 100, -5.7 ] + - [ 76476.0, 100, -7.0 ] + - [ 76600.0, 100, -6.4 ] + - [ 76601.0, 90, -6.4 ] + - [ 76726.0, 90, -6.2 ] + - [ 77256.0, 90, -2.1 ] + - [ 77285.0, 80, -2.1 ] + - [ 77299.0, 80, -14.0 ] + - [ 77331.0, 80, -1.6 ] + - [ 77379.0, 90, -1.6 ] + - [ 77425.0, 110, -1.6 ] + - [ 77455.0, 110, -4.9 ] + - [ 77498.0, 110, 0.0 ] + - [ 77505.0, 160, 0.0 ] + - [ 77555.0, 160, -5.6 ] + - [ 77662.0, 160, 0.0 ] + - [ 78085.0, 160, -4.0 ] + - [ 78223.0, 160, -6.8 ] + - [ 78337.0, 130, -6.8 ] + - [ 78856.0, 130, -6.6 ] + - [ 78875.0, 130, -7.2 ] + - [ 79345.0, 150, -7.2 ] + - [ 79600.0, 150, -6.5 ] + - [ 79792.0, 150, -0.2 ] + - [ 80537.0, 150, -5.2 ] + - [ 81300.0, 150, -4.8 ] + - [ 81634.0, 110, -4.8 ] + - [ 81943.0, 110, -5.4 ] + - [ 82166.0, 110, 0.0 ] + - [ 82408.0, 110, 4.8 ] + - [ 82790.0, 110, 5.9 ] + - [ 83137.0, 120, 5.9 ] + - [ 83300.0, 120, 0.0 ] + - [ 83519.0, 150, 0.0 ] + - [ 83597.0, 150, -7.8 ] + - [ 83827.0, 150, -8.1 ] + - [ 84150.0, 150, -7.2 ] + - [ 84391.0, 150, 0.0 ] + - [ 84966.0, 150, -4.0 ] + - [ 85529.0, 130, -2.3 ] + - [ 85589.0, 130, -4.0 ] + - [ 86081.0, 130, 0.0 ] + - [ 86514.0, 130, 7.8 ] + - [ 86577.0, 120, 7.8 ] + - [ 87554.0, 90, 7.8 ] + - [ 87690.0, 90, 7.9 ] + - [ 87842.0, 90, 1.7 ] + - [ 88007.0, 110, 1.7 ] + - [ 88100.0, 110, 3.4 ] + - [ 88260.0, 110, 5.3 ] + - [ 88376.0, 160, 5.3 ] + - [ 88450.0, 160, 6.7 ] + - [ 89050.0, 160, 7.4 ] + - [ 89350.0, 160, -7.1 ] + - [ 90365.0, 160, 0.0 ] + - [ 90700.0, 160, -7.3 ] + - [ 92000.0, 160, -7.9 ] + - [ 92166.0, 160, -4.0 ] + - [ 92460.0, 160, -5.4 ] + - [ 93330.0, 160, 0.0 ] + - [ 93901.0, 160, 0.7 ] + - [ 94156.0, 160, 5.2 ] + - [ 94440.0, 160, -2.8 ] + - [ 94530.0, 160, -0.8 ] + - [ 94630.0, 160, -6.8 ] + - [ 94830.0, 160, -4.4 ] + - [ 95090.0, 160, -4.6 ] + - [ 95500.0, 160, -4.8 ] + - [ 96500.0, 160, -4.4 ] + - [ 96700.0, 160, -5.6 ] + - [ 97000.0, 160, -4.6 ] + - [ 97590.0, 160, 0.0 ] + - [ 97858.0, 120, 0.0 ] + - [ 98224.0, 120, -1.5 ] + - [ 98264.0, 120, 0.0 ] + - [ 98577.0, 120, 7.5 ] + - [ 98738.0, 120, 0.0 ] + - [ 99055.0, 130, 0.0 ] + - [ 99427.0, 130, -2.0 ] + - [ 99610.0, 130, -3.1 ] + - [ 99906.0, 120, -3.1 ] + - [ 99980.0, 120, -1.3 ] + - [100190.0, 120, -6.8 ] + - [100832.0, 120, -7.2 ] + - [100980.0, 120, -8.1 ] + - [101100.0, 120, -7.4 ] + - [101245.0, 120, -6.3 ] + - [101332.0, 100, -6.3 ] + - [101365.0, 100, -2.4 ] + - [101551.0, 110, -2.4 ] + - [101800.0, 110, 0.0 ] diff --git a/test/data/paths/slope.yaml b/test/data/paths/slope.yaml index 5922df6..00371a7 100644 --- a/test/data/paths/slope.yaml +++ b/test/data/paths/slope.yaml @@ -1,27 +1,29 @@ %YAML 1.2 --- -path: - name: "10 km, different gradient, 160 km/h" - id: slope - UUID: ffd243a9-0223-4210-8c7d-e6c90fde70d3 - points_of_interest: - # [ station in m, name, front or rear ] - - [ 850.00, view_point_1, front ] - - [ 1000.00, distant_signal_1, front ] - - [ 2000.00, main_signal_1, front ] - - [ 9000.00, main_signal_3, front ] - - [ 9050.00, clearing_point_1, rear ] - characteristic_sections: - # [ station in m, speed limit in km/h, resistance in ‰ ] - - [ 0.0, 160, 0.00 ] - - [ 1000.0, 160, 1.00 ] - - [ 2000.0, 160, 2.00 ] - - [ 3000.0, 160, 5.00 ] - - [ 4000.0, 160, -3.00 ] - - [ 5000.0, 160, 5.00 ] - - [ 6000.0, 160, -10.00 ] - - [ 7000.0, 160, 15.00 ] - - [ 8000.0, 160, -10.00 ] - - [ 8500.0, 160, 20.00 ] - - [ 9000.0, 160, 0.00 ] - - [ 10000.0, 160, 0.00 ] +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2022.05" +paths: + - name: "10 km, different gradient, 160 km/h" + id: slope + UUID: ffd243a9-0223-4210-8c7d-e6c90fde70d3 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 850.00, view_point_1, front ] + - [ 1000.00, distant_signal_1, front ] + - [ 2000.00, main_signal_1, front ] + - [ 9000.00, main_signal_3, front ] + - [ 9050.00, clearing_point_1, rear ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 1000.0, 160, 1.00 ] + - [ 2000.0, 160, 2.00 ] + - [ 3000.0, 160, 5.00 ] + - [ 4000.0, 160, -3.00 ] + - [ 5000.0, 160, 5.00 ] + - [ 6000.0, 160, -10.00 ] + - [ 7000.0, 160, 15.00 ] + - [ 8000.0, 160, -10.00 ] + - [ 8500.0, 160, 20.00 ] + - [ 9000.0, 160, 0.00 ] + - [ 10000.0, 160, 0.00 ] diff --git a/test/data/paths/speed.yaml b/test/data/paths/speed.yaml index d35e33c..378e33f 100644 --- a/test/data/paths/speed.yaml +++ b/test/data/paths/speed.yaml @@ -1,27 +1,29 @@ %YAML 1.2 --- -path: - name: "10 km, no gradient, different speed limits" - id: speed - UUID: 401b8ce7-fa75-4576-8a4a-43be2eb55e50 - points_of_interest: - # [ station in m, name, front or rear ] - - [ 999.00, point_1, front ] - - [ 2000.00, point_2, front ] - - [ 3333.30, point_3, rear ] - - [ 5000.00, point_4, front ] - - [ 7777.00, point_5, front ] - - [ 9000.00, point_6, front ] - - [ 9500.95, point_7, front ] - characteristic_sections: - # [ station in m, speed limit in km/h, resistance in ‰ ] - - [ 0.0, 160, 0.00 ] - - [ 3000.0, 60, 0.00 ] - - [ 4000.0, 160, 0.00 ] - - [ 5000.0, 60, 0.00 ] - - [ 6000.0, 160, 0.00 ] - - [ 6500.0, 60, 0.00 ] - - [ 6700.0, 65, 0.00 ] - - [ 6800.0, 70, 0.00 ] - - [ 7000.0, 120, 0.00 ] - - [ 10000.0, 160, 0.00 ] +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2022.05" +paths: + - name: "10 km, no gradient, different speed limits" + id: speed + UUID: 401b8ce7-fa75-4576-8a4a-43be2eb55e50 + points_of_interest: + # [ station in m, name, front or rear ] + - [ 999.00, point_1, front ] + - [ 2000.00, point_2, front ] + - [ 3333.30, point_3, rear ] + - [ 5000.00, point_4, front ] + - [ 7777.00, point_5, front ] + - [ 9000.00, point_6, front ] + - [ 9500.95, point_7, front ] + characteristic_sections: + # [ station in m, speed limit in km/h, resistance in ‰ ] + - [ 0.0, 160, 0.00 ] + - [ 3000.0, 60, 0.00 ] + - [ 4000.0, 160, 0.00 ] + - [ 5000.0, 60, 0.00 ] + - [ 6000.0, 160, 0.00 ] + - [ 6500.0, 60, 0.00 ] + - [ 6700.0, 65, 0.00 ] + - [ 6800.0, 70, 0.00 ] + - [ 7000.0, 120, 0.00 ] + - [ 10000.0, 160, 0.00 ] From d4e7806efc05acccd3721e9978bb68d843a81d28 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Sun, 8 May 2022 21:43:21 +0200 Subject: [PATCH 12/18] separate file for constructors --- CONTRIBUTING.md | 10 +- src/TrainRuns.jl | 1 + src/constructors.jl | 405 ++++++++++++++++++++++++++++++++++++++++++++ src/types.jl | 401 ------------------------------------------- 4 files changed, 414 insertions(+), 403 deletions(-) create mode 100644 src/constructors.jl diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbc6ddb..df36fce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,9 +34,15 @@ julia> # use the ] key (TrainRuns) pkg> ``` -## TrainRuns files +## Files in TrainRuns -TODO! +| file | concern | +| --------------- | ------------------------------ | +| TrainRuns.jl | main file and function | +| types.jl | special TrainRuns types | +| constructors.jl | type constructors | +| formulary.jl | formulars from literature | +| output.jl | transformation into DataFrames | ## Reporting Issues diff --git a/src/TrainRuns.jl b/src/TrainRuns.jl index eca0419..e511c52 100644 --- a/src/TrainRuns.jl +++ b/src/TrainRuns.jl @@ -19,6 +19,7 @@ trainrun, Path, Settings, exportToCsv ## include package files include("types.jl") +include("constructors.jl") include("formulary.jl") include("characteristics.jl") include("behavior.jl") diff --git a/src/constructors.jl b/src/constructors.jl new file mode 100644 index 0000000..f795ed4 --- /dev/null +++ b/src/constructors.jl @@ -0,0 +1,405 @@ +#!/usr/bin/env julia +# -*- coding: UTF-8 -*- +# __julia-version__ = 1.7.2 +# __author__ = "Martin Scheidt" +# __copyright__ = "2022" +# __license__ = "ISC" + +""" + Settings(file) + +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, ".") +``` +""" +function Settings(file="DEFAULT") + + ## default values + massModel = :mass_point + stepVariable = :distance + stepSize = 20 + approxLevel = 3 + outputDetail = :running_time + outputFormat = :julia_dict + outputDir = "." + + ## load from file + 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 + + Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) + +end #function Settings() # constructor + + +""" + Path(file, type = :YAML) + +Path is a datastruture for calculation context. +The function Path() will create a running path for the train. +Supported formats are: railtoolkit/schema (2022.05) + +# Example +```jldoctest +julia> my_path = Path("file.yaml") # will generate a path from a YAML file. +Path(variables) +``` +""" +function Path(file, type = :YAML) + + ## default values + name = "" + id = "" + uuid = UUIDs.uuid4() + poi = [] + sections = [] + + ## process flags + POI_PRESENT = false + + ## load from file + if type == :YAML + + data = YAML.load(open(file)) + if data["schema"] != "https://railtoolkit.org/schema/running-path.json" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/running-path (2022.05)") + end + if data["schema_version"] != "2022.05" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/running-path (2022.05)") + end + + ## JSON schema for YAML-file validation + railtoolkit_schema = Schema("""{ + "required": [ "schema", "schema_version", "paths" ], + "properties": { + "schema": { + "description": "Identifier of the schema", + "enum": [ "https://railtoolkit.org/schema/running-path.json" ] + }, + "schema_version": { + "description": "Version of the schema", + "type": "string", + "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" + }, + "paths": { + "type": "array", + "minItems": 1, + "items": { + "required": [ "name", "id", "characteristic_sections" ], + "type": "object", + "properties": { + "characteristic_sections": { + "description": "", + "type": "array", + "minItems": 2, + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { + "description": "milage in meter", + "type": "number" + }, + { + "description": "speed in kilometers per hour", + "type": "number", + "exclusiveMinimum": 0 + }, + { + "description": "resistance in permil", + "type": "number" + } + ] + } + }, + "id": { + "description": "Identifier of the path", + "type": "string" + }, + "name": { + "description": "Name of the path", + "type": "string" + }, + "points_of_interest": { + "description": "", + "type": "array", + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "description": "", + "prefixItems": [ + { "type": "number" }, + { "type": "string" }, + { "enum": [ "front", "rear" ] } + ] + } + }, + "UUID": { + "description": "The unique identifier for a path", + "type": "string", + "format": "uuid" + } + } + } + } + } + }""") + + paths = data["paths"] + try + validate(railtoolkit_schema, paths) + catch err + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/running-path (2022.05)") + end + if length(paths) > 1 + println("WARNING: the loaded file contains more than one path. Using only the first!") + end + path = paths[1] + + ## set the variables if they exist in "settings" + # required + name = path["name"] + id = path["id"] + tmp_sec = path["characteristic_sections"] + # optional + haskey(path, "UUID") ? uuid = parse(UUID, path["UUID"] ) : nothing + haskey(path, "points_of_interest") ? POI_PRESENT = true : nothing + haskey(path, "points_of_interest") ? tmp_points = path["points_of_interest"] : nothing + + else + error("Unknown file type '$type'") + end #if type + + ## process characteristic sections + sort!(tmp_sec, by = x -> x[1]) + for row in 2:length(tmp_sec) + s_start = tmp_sec[row-1][1] # first point of the section (in m) + s_end = tmp_sec[row][1] # first point of the next section (in m) + v_limit = tmp_sec[row-1][2]/3.6 # paths speed limt (in m/s) + f_Rp = tmp_sec[row-1][3] # specific path resistance of the section (in ‰) + + section = Dict(:s_start => s_start, + :s_end => s_end, + :v_limit => v_limit, + :f_Rp => f_Rp) + push!(sections, section) + end #for row + # s_start in first entry defines the path's beginning + # s_end in last entry defines the path's ending + + ## process points of interest + if POI_PRESENT + sort!(tmp_points, by = x -> x[1]) + for elem in tmp_points + station = elem[1] # first point of the section (in m) + label = elem[2] # paths speed limt (in m/s) + measure = elem[3] # specific path resistance of the section (in ‰) + + point = Dict(:station => station, + :label => label, + :measure => measure) + push!(poi, point) + end #for elem + end #if !isempty(points) + + Path(name, id, uuid, poi, sections) + +end #function Path() # constructor + +## create a moving section containing characteristic sections +function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) + # this function creates and returns a moving section dependent on the paths attributes + + s_entry = path.sections[1][:s_start] # first position (in m) + s_exit = path.sections[end][:s_end] # last position (in m) + pathLength = s_exit - s_entry # total length (in m) + + CSs=Vector{Dict}() + s_csStart=s_entry + csId=1 + for row in 2:length(path.sections) + previousSection = path.sections[row-1] + currentSection = path.sections[row] + speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) + pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] + if speedLimitIsDifferent || pathResistanceIsDifferent + # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] + push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path)) + s_csStart = currentSection[:s_start] + csId = csId+1 + end #if + end #for + push!(CSs, createCharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path)) + + movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore + :length => pathLength, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => s_exit, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :characteristicSections => CSs) # list of containing characteristic sections + + return movingSection +end #function createMovingSection + +## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. +function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path) + # Create and return a characteristic section dependent on the paths attributes + characteristicSection= Dict(:id => id, # identifier + :s_entry => s_entry, # first position (in m) + :s_exit => section[:s_end], # last position (in m) + :length => section[:s_end] -s_entry, # total length (in m) + :r_path => section[:f_Rp], # path resistance (in ‰) + :behaviorSections => Dict(), # list of containing behavior sections + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_limit => v_limit, # speed limit (in m/s) + # initializing :v_entry, :v_peak and :v_exit with :v_limit + :v_peak => v_limit, # maximum reachable speed (in m/s) + :v_entry => v_limit, # maximum entry speed (in m/s) + :v_exit => v_limit) # maximum exit speed (in m/s) + + # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated + s_exit = characteristicSection[:s_exit] + + ##TODO: use a tuple with naming + pointsOfInterest = Tuple[] + # pointsOfInterest = Real[] + if !isempty(path.poi) + for POI in path.poi + s_poi = POI[:station] + if POI[:measure] == "rear" + s_poi -= s_trainLength + end + if s_entry < s_poi && s_poi < s_exit + push!(pointsOfInterest, (s_poi, POI[:label]) ) + # push!(pointsOfInterest, s_poi ) + end + end + end + push!(pointsOfInterest, (s_exit,"")) # s_exit has to be the last POI so that there will always be a POI to campare the current position with + # push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with + + merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) + + return characteristicSection +end #function createCharacteristicSection + +""" +a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points +""" +function createDataPoint() + dataPoint = Dict( + :i => 0, # identifier and counter variable of the driving course + :behavior => "", # type of behavior section the data point is part of - see createBehaviorSection() + # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter + :s => 0.0, # position (in m) + :Δs => 0.0, # step size (in m) + :t => 0.0, # point in time (in s) + :Δt => 0.0, # step size (in s) + :v => 0.0, # velocity (in m/s) + :Δv => 0.0, # step size (in m/s) + :a => 0.0, # acceleration (in m/s^2) + :W => 0.0, # mechanical work (in Ws) + :ΔW => 0.0, # mechanical work in this step (in Ws) + :E => 0.0, # energy consumption (in Ws) + :ΔE => 0.0, # energy consumption in this step (in Ws) + :F_T => 0.0, # tractive effort (in N) + :F_R => 0.0, # resisting force (in N) + :R_path => 0.0, # path resistance (in N) + :R_train => 0.0, # train resistance (in N) + :R_traction => 0.0, # traction unit resistance (in N) + :R_wagons => 0.0, # set of wagons resistance (in N) + :label => "" # a label for importend points + ) + return dataPoint +end #function createDataPoint + +""" +BehaviorSection() TODO! +""" +function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) + BS= Dict( + :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" + :length => 0.0, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => 0.0, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_entry => v_entry, # entry speed (in m/s) + :v_exit => 0.0, # exit speed (in m/s) + :dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point + ) + return BS +end #function createBehaviorSection diff --git a/src/types.jl b/src/types.jl index b457958..0123f15 100644 --- a/src/types.jl +++ b/src/types.jl @@ -5,19 +5,6 @@ # __copyright__ = "2022" # __license__ = "ISC" -""" - Settings(file) - -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 massModel::Symbol # model type of train mass ":mass_point" or ":homogeneous_strip". @@ -29,95 +16,8 @@ struct Settings 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 = "." - - ## load from file - 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 -""" - Path(file, type = :YAML) - -Path is a datastruture for calculation context. -The function Path() will create a running path for the train. -Supported formats are: railtoolkit/schema (2022.05) - -# Example -```jldoctest -julia> my_path = Path("file.yaml") # will generate a path from a YAML file. -Path(variables) -``` -""" struct Path name::String # a name or description of the path @@ -126,309 +26,8 @@ struct Path poi::Vector # a vector of triples with points along the path sections::Vector # a vector of the characteristic sections - ## constructor - function Path(file, type = :YAML) - - ## default values - name = "" - id = "" - uuid = UUIDs.uuid4() - poi = [] - sections = [] - - ## process flags - POI_PRESENT = false - - ## load from file - if type == :YAML - - data = YAML.load(open(file)) - if data["schema"] != "https://railtoolkit.org/schema/running-path.json" - error("Could not load path file '$file'.\n - YAML format is not recognized. - Currently supported: railtoolkit/schema/running-path (2022.05)") - end - if data["schema_schema"] != "2022.05" - error("Could not load path file '$file'.\n - YAML format is not recognized. - Currently supported: railtoolkit/schema/running-path (2022.05)") - end - - ## JSON schema for YAML-file validation - railtoolkit_schema = Schema("""{ - "required": [ "schema", "schema_version", "paths" ], - "properties": { - "schema": { - "description": "Identifier of the schema", - "enum": [ "https://railtoolkit.org/schema/running-path.json" ] - }, - "schema_version": { - "description": "Version of the schema", - "type": "string", - "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" - }, - "paths": { - "type": "array", - "minItems": 1, - "items": { - "required": [ "name", "id", "characteristic_sections" ], - "type": "object", - "properties": { - "characteristic_sections": { - "description": "", - "type": "array", - "minItems": 2, - "uniqueItems": true, - "items": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "description": "", - "prefixItems": [ - { - "description": "milage in meter", - "type": "number" - }, - { - "description": "speed in kilometers per hour", - "type": "number", - "exclusiveMinimum": 0 - }, - { - "description": "resistance in permil", - "type": "number" - } - ] - } - }, - "id": { - "description": "Identifier of the path", - "type": "string" - }, - "name": { - "description": "Name of the path", - "type": "string" - }, - "points_of_interest": { - "description": "", - "type": "array", - "uniqueItems": true, - "items": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "description": "", - "prefixItems": [ - { "type": "number" }, - { "type": "string" }, - { "enum": [ "front", "rear" ] } - ] - } - }, - "UUID": { - "description": "The unique identifier for a path", - "type": "string", - "format": "uuid" - } - } - } - } - } - }""") - - paths = data["paths"] - try - validate(railtoolkit_schema, paths) - catch err - error("Could not load path file '$file'.\n - YAML format is not recognized. - Currently supported: railtoolkit/schema/running-path (2022.05)") - end - if length(paths) > 1 - println("WARNING: the loaded file contains more than one path. Using only the first!") - end - path = paths[1] - - ## set the variables if they exist in "settings" - # required - name = path["name"] - id = path["id"] - tmp_sec = path["characteristic_sections"] - # optional - haskey(path, "UUID") ? uuid = parse(UUID, path["UUID"] ) : nothing - haskey(path, "points_of_interest") ? POI_PRESENT = true : nothing - haskey(path, "points_of_interest") ? tmp_points = path["points_of_interest"] : nothing - - else - error("Unknown file type '$type'") - end #if type - - ## process characteristic sections - sort!(tmp_sec, by = x -> x[1]) - for row in 2:length(tmp_sec) - s_start = tmp_sec[row-1][1] # first point of the section (in m) - s_end = tmp_sec[row][1] # first point of the next section (in m) - v_limit = tmp_sec[row-1][2]/3.6 # paths speed limt (in m/s) - f_Rp = tmp_sec[row-1][3] # specific path resistance of the section (in ‰) - - section = Dict(:s_start => s_start, - :s_end => s_end, - :v_limit => v_limit, - :f_Rp => f_Rp) - push!(sections, section) - end #for row - # s_start in first entry defines the path's beginning - # s_end in last entry defines the path's ending - - ## process points of interest - if POI_PRESENT - sort!(tmp_points, by = x -> x[1]) - for elem in tmp_points - station = elem[1] # first point of the section (in m) - label = elem[2] # paths speed limt (in m/s) - measure = elem[3] # specific path resistance of the section (in ‰) - - point = Dict(:station => station, - :label => label, - :measure => measure) - push!(poi, point) - end #for elem - end #if !isempty(points) - - new(name, id, uuid, poi, sections) - - end #function Path - end #struct Path -""" -a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points -""" -function createDataPoint() - dataPoint = Dict( - :i => 0, # identifier and counter variable of the driving course - :behavior => "", # type of behavior section the data point is part of - see createBehaviorSection() - # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter - :s => 0.0, # position (in m) - :Δs => 0.0, # step size (in m) - :t => 0.0, # point in time (in s) - :Δt => 0.0, # step size (in s) - :v => 0.0, # velocity (in m/s) - :Δv => 0.0, # step size (in m/s) - :a => 0.0, # acceleration (in m/s^2) - :W => 0.0, # mechanical work (in Ws) - :ΔW => 0.0, # mechanical work in this step (in Ws) - :E => 0.0, # energy consumption (in Ws) - :ΔE => 0.0, # energy consumption in this step (in Ws) - :F_T => 0.0, # tractive effort (in N) - :F_R => 0.0, # resisting force (in N) - :R_path => 0.0, # path resistance (in N) - :R_train => 0.0, # train resistance (in N) - :R_traction => 0.0, # traction unit resistance (in N) - :R_wagons => 0.0, # set of wagons resistance (in N) - :label => "" # a label for importend points - ) - return dataPoint -end #function createDataPoint - -""" -BehaviorSection() TODO! -""" -function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) - BS= Dict( - :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" - :length => 0.0, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => 0.0, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_entry => v_entry, # entry speed (in m/s) - :v_exit => 0.0, # exit speed (in m/s) - :dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point - ) - return BS -end #function createBehaviorSection - -## create a moving section containing characteristic sections -function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) - # this function creates and returns a moving section dependent on the paths attributes - - s_entry = path.sections[1][:s_start] # first position (in m) - s_exit = path.sections[end][:s_end] # last position (in m) - pathLength = s_exit - s_entry # total length (in m) - - CSs=Vector{Dict}() - s_csStart=s_entry - csId=1 - for row in 2:length(path.sections) - previousSection = path.sections[row-1] - currentSection = path.sections[row] - speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) - pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] - if speedLimitIsDifferent || pathResistanceIsDifferent - # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] - push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path)) - s_csStart = currentSection[:s_start] - csId = csId+1 - end #if - end #for - push!(CSs, createCharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path)) - - movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore - :length => pathLength, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => s_exit, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :characteristicSections => CSs) # list of containing characteristic sections - - return movingSection -end #function createMovingSection - - -## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. -function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path) - # Create and return a characteristic section dependent on the paths attributes - characteristicSection= Dict(:id => id, # identifier - :s_entry => s_entry, # first position (in m) - :s_exit => section[:s_end], # last position (in m) - :length => section[:s_end] -s_entry, # total length (in m) - :r_path => section[:f_Rp], # path resistance (in ‰) - :behaviorSections => Dict(), # list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => v_limit, # speed limit (in m/s) - # initializing :v_entry, :v_peak and :v_exit with :v_limit - :v_peak => v_limit, # maximum reachable speed (in m/s) - :v_entry => v_limit, # maximum entry speed (in m/s) - :v_exit => v_limit) # maximum exit speed (in m/s) - - # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated - s_exit = characteristicSection[:s_exit] - - ##TODO: use a tuple with naming - pointsOfInterest = Tuple[] - # pointsOfInterest = Real[] - if !isempty(path.poi) - for POI in path.poi - s_poi = POI[:station] - if POI[:measure] == "rear" - s_poi -= s_trainLength - end - if s_entry < s_poi && s_poi < s_exit - push!(pointsOfInterest, (s_poi, POI[:label]) ) - # push!(pointsOfInterest, s_poi ) - end - end - end - push!(pointsOfInterest, (s_exit,"")) # s_exit has to be the last POI so that there will always be a POI to campare the current position with - # push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with - - merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) - - return characteristicSection -end #function createCharacteristicSection - """ Read the train information from a YAML file, save it in a train Dict and return it. """ From d750da80fb83941ad358778d54ef37ca4d68f284 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Sun, 8 May 2022 21:44:40 +0200 Subject: [PATCH 13/18] Updated train schema to 2022.05 --- test/data/trains/freight.yaml | 225 ++++++++-------- test/data/trains/local.yaml | 295 ++++++++++----------- test/data/trains/longdistance.yaml | 402 +++++++++++++++-------------- 3 files changed, 481 insertions(+), 441 deletions(-) diff --git a/test/data/trains/freight.yaml b/test/data/trains/freight.yaml index 59c82bb..c23db6f 100644 --- a/test/data/trains/freight.yaml +++ b/test/data/trains/freight.yaml @@ -1,112 +1,123 @@ %YAML 1.2 --- -train: - name: "V 90 with 10 ore wagons of type Facs 124" # (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 and https://dybas.de/dybas/gw/gw_f_1/g124.html) - length: 205.3 # in m (source: FBS: DB 290 with 10x Facs124) - m_td: 80000 # mass on driving axles of the traction unit in kg (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - m_tc: 0 # mass on carrying axles of the traction unit in kg (no carrying axles; source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - m_w: 850000 # mass of the consist (set of wagons) in kg (source: FBS: 10x Facs124) - rotationMassFactor_train: - rotationMassFactor_t: 1.09 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit) - rotationMassFactor_w: 1.03 # (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Güterwagenzug beladen" -> 1.03 to 1.04) - powerType: diesel # diesel or electric (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - type: freight # "freight" or "passenger" (source: https://dybas.de/dybas/gw/gw_f_1/g124.html) - v_limit: # in m/s - v_limit_kmh: 80 # in km/h (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - a_braking: -0.4124 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 290 with with 10x Facs124) +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2022.05" +trains: + - name: "V 90 with 10 ore wagons of type Facs 124" # (source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 and https://dybas.de/dybas/gw/gw_f_1/g124.html) + id: Fr100 + formation: [DB_V90,Facs124,Facs124,Facs124,Facs124,Facs124,Facs124,Facs124,Facs124,Facs124,Facs124] - # coefficients for the vehicle resistance - # for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_t)/v00)^2) - f_Rtd0: 2.2 # coefficient for basic resistance due to the traction units driving axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsige Diesellokomot." -> 2.2 ‰ to 3.5 ‰) - f_Rtc0: 0 # coefficient for basic resistance due to the traction units carring axles (in ‰) (source: no carrying axles; source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90) - F_Rt2: 7500 # coefficient for air resistance of the traction units (in N) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "MittelfUhrerstand" -> 5000 N to 10000 N) +vehicles: + - name: "Facs 124" # source: https://dybas.de/dybas/gw/gw_f_1/g124.html + id: Facs124 + UUID: 30abe88d-5be7-4c9c-b6a2-61c6b0e9f9bc + picture: https://dybas.de/dybas/gw/gw_f_1/image/124_0042.jpg + vehicle_type: freight # "freight", "passenger", "traction unit" or "multiple unit" - # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_w)/v00)^2)) - f_Rw0: 1.4 # coefficient for the consists basic resistance (in ‰) (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for "roller bearings") - f_Rw1: 0 # coefficient for the consists resistance to rolling (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 -> no f_Rw1 for freight consists) - f_Rw2: 3.9 # coefficient fo the consistsr air resistance (in ‰) (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for "full train loads of coal or ore" modified for the used formula) + length: 19.04 # source: https://dybas.de/dybas/gw/gw_f_1/g124.html + mass: 25.00 # source: https://dybas.de/dybas/gw/gw_f_1/g124.html + load_limit: 59.0 # source: https://dybas.de/dybas/gw/gw_f_1/g124.html + speed_limit: 100 # source: https://dybas.de/dybas/gw/gw_f_1/g124.html - # tractive effort as pairs of speed and tractive effort - F_T_pairs: # [v in m/s, F_T in N] - F_T_pairs_kmh: # [v in km/h, F_T in N] (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 290 with with 10x Facs124) - - [0.0, 186940] - - [1.0, 186940] - - [2.0, 182310] - - [3.0, 177680] - - [4.0, 173050] - - [5.0, 168420] - - [6.0, 163790] - - [7.0, 159160] - - [8.0, 154530] - - [9.0, 149240] - - [10.0, 144120] - - [11.0, 139150] - - [12.0, 134340] - - [13.0, 129690] - - [14.0, 125200] - - [15.0, 120860] - - [16.0, 116680] - - [17.0, 112660] - - [18.0, 108790] - - [19.0, 105080] - - [20.0, 101530] - - [21.0, 98120] - - [22.0, 94840] - - [23.0, 91700] - - [24.0, 88700] - - [25.0, 85840] - - [26.0, 83110] - - [27.0, 80520] - - [28.0, 78070] - - [29.0, 75750] - - [30.0, 73580] - - [31.0, 71600] - - [32.0, 69660] - - [33.0, 67770] - - [34.0, 65930] - - [35.0, 64130] - - [36.0, 62380] - - [37.0, 60670] - - [38.0, 59010] - - [39.0, 57400] - - [40.0, 55830] - - [41.0, 54300] - - [42.0, 52820] - - [43.0, 51390] - - [44.0, 50000] - - [45.0, 48660] - - [46.0, 48080] - - [47.0, 47220] - - [48.0, 46380] - - [49.0, 45550] - - [50.0, 44730] - - [51.0, 43930] - - [52.0, 43140] - - [53.0, 42370] - - [54.0, 41610] - - [55.0, 40870] - - [56.0, 40140] - - [57.0, 39430] - - [58.0, 38730] - - [59.0, 38040] - - [60.0, 37370] - - [61.0, 36720] - - [62.0, 36070] - - [63.0, 35450] - - [64.0, 34830] - - [65.0, 34230] - - [66.0, 33650] - - [67.0, 33080] - - [68.0, 32520] - - [69.0, 31980] - - [70.0, 31450] - - [71.0, 30940] - - [72.0, 30440] - - [73.0, 29960] - - [74.0, 29490] - - [75.0, 29030] - - [76.0, 28590] - - [77.0, 28170] - - [78.0, 27760] - - [79.0, 27360] - - [80.0, 26980] + rotation_mass: 1.03 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Güterwagenzug beladen" -> 1.03 to 1.04) + base_resistance: 1.4 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for "roller bearings") + air_resistance: 3.9 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for "full train loads of coal or ore" modified for the used formula + + - name: "DB V90" # source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 + id: DB_V90 + UUID: 48b99596-98b2-40c8-b31a-3f9266013803 + picture: https://commons.wikimedia.org/wiki/File:DB_294_-_594_side_view.jpg + vehicle_type: traction unit # "freight", "passenger", "traction unit" or "multiple unit" + power_type: diesel # "diesel", "electric", or "steam" + + length: 14.32 # source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 + mass: 80 # source: https://de.wikipedia.org/wiki/DB-Baureihe_V_90 + mass_traction: 80 # in t # mass on driving axles of the traction unit + speed_limit: 80 # in km/h + + rotation_mass: 1.09 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit + base_resistance: 2.2 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsige Diesellokomot." -> 2.2 ‰ to 3.5 ‰ + air_resistance: 0.01 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "MittelfUhrerstand" -> 5000 N to 10000 N; modified for the used formula + + tractive_effort: + - [0.0, 186940] + - [1.0, 186940] + - [2.0, 182310] + - [3.0, 177680] + - [4.0, 173050] + - [5.0, 168420] + - [6.0, 163790] + - [7.0, 159160] + - [8.0, 154530] + - [9.0, 149240] + - [10.0, 144120] + - [11.0, 139150] + - [12.0, 134340] + - [13.0, 129690] + - [14.0, 125200] + - [15.0, 120860] + - [16.0, 116680] + - [17.0, 112660] + - [18.0, 108790] + - [19.0, 105080] + - [20.0, 101530] + - [21.0, 98120] + - [22.0, 94840] + - [23.0, 91700] + - [24.0, 88700] + - [25.0, 85840] + - [26.0, 83110] + - [27.0, 80520] + - [28.0, 78070] + - [29.0, 75750] + - [30.0, 73580] + - [31.0, 71600] + - [32.0, 69660] + - [33.0, 67770] + - [34.0, 65930] + - [35.0, 64130] + - [36.0, 62380] + - [37.0, 60670] + - [38.0, 59010] + - [39.0, 57400] + - [40.0, 55830] + - [41.0, 54300] + - [42.0, 52820] + - [43.0, 51390] + - [44.0, 50000] + - [45.0, 48660] + - [46.0, 48080] + - [47.0, 47220] + - [48.0, 46380] + - [49.0, 45550] + - [50.0, 44730] + - [51.0, 43930] + - [52.0, 43140] + - [53.0, 42370] + - [54.0, 41610] + - [55.0, 40870] + - [56.0, 40140] + - [57.0, 39430] + - [58.0, 38730] + - [59.0, 38040] + - [60.0, 37370] + - [61.0, 36720] + - [62.0, 36070] + - [63.0, 35450] + - [64.0, 34830] + - [65.0, 34230] + - [66.0, 33650] + - [67.0, 33080] + - [68.0, 32520] + - [69.0, 31980] + - [70.0, 31450] + - [71.0, 30940] + - [72.0, 30440] + - [73.0, 29960] + - [74.0, 29490] + - [75.0, 29030] + - [76.0, 28590] + - [77.0, 28170] + - [78.0, 27760] + - [79.0, 27360] + - [80.0, 26980] diff --git a/test/data/trains/local.yaml b/test/data/trains/local.yaml index 666a37e..5c380e8 100644 --- a/test/data/trains/local.yaml +++ b/test/data/trains/local.yaml @@ -1,152 +1,153 @@ %YAML 1.2 --- -train: - name: "Siemens Desiro Classic" # (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - length: 41.7 # in m (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - m_td: 52800 # mass on driving axles of the traction unit in kg (source: FBS: DB 642; proportionately to the number of axles: 4 to 2, see: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - m_tc: 35200 # mass on carrying axles of the traction unit in kg (source: FBS: DB 642; proportionately to the number of axles: 4 to 2, see: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - m_w: 0 # mass of the consist (set of wagons) in kg (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) - rotationMassFactor_train: 1.08 # (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Zug, überschlägliche Berechnung") - rotationMassFactor_t: - rotationMassFactor_w: - powerType: diesel # diesel or electric (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - type: passenger # "freight" or "passenger" (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - v_limit: # in m/s - v_limit_kmh: 120 # in km/h (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - a_braking: -0.4253 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 642) +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2022.05" +trains: + - name: Regional Train + id: RB50-1 + formation: [DB_BR_642] - # coefficients for the vehicle resistance - # for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_t)/v00)^2) - f_Rtd0: 3.0 # coefficient for basic resistance due to the traction units driving axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WL0" -> 2.5 ‰ to 3.5 ‰) - f_Rtc0: 1.4 # coefficient for basic resistance due to the traction units carring axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WW0" -> 1.2 ‰ to 1.6 ‰) - F_Rt2: 2600 # coefficient for air resistance of the traction units (in N) (source: the closest parameters are used: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "Fzg. vierachsig, abgerundeter Kopf" plus "Sektion bei Mehrteiligkeit" -> 2200 N + 400 N) +vehicles: + - name: Siemens Desiro Classic # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + id: DB_BR_642 + UUID: c915c80d-c63d-490b-879f-c481e4b62b55 + picture: https://commons.wikimedia.org/wiki/File:Liesel_28-11-10_642_055-8_im_Bahnhof_Scharfenstein.JPG + power_type: diesel # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + vehicle_type: multiple unit # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + + length: 41.7 # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + mass: 68.0 # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + load_limit: 20.0 # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + mass_traction: 45.333 # proportionately to the number of axles: 4 to 2, see: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) + speed_limit: 120 # source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic + a_braking: -0.4253 # - # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_w)/v00)^2)) - f_Rw0: # coefficient for basic resistance of the set of wagons (in ‰) (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) - f_Rw1: # coefficient for resistance to rolling of the set of wagons (in ‰) (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) - f_Rw2: # coefficient for air resistance of the set of wagons (in ‰) (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic -> no separate wagons) + # coefficients for the vehicle resistance + rotation_mass: 1.08 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Zug, überschlägliche Berechnung" + base_resistance: 3.0 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WL0" -> 2.5 ‰ to 3.5 ‰ + rolling_resistance: 1.4 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WW0" -> 1.2 ‰ to 1.6 ‰ + air_resistance: 0.0039 # source: the closest parameters are used: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "Fzg. vierachsig, abgerundeter Kopf" plus "Sektion bei Mehrteiligkeit" -> 2200 N + 400 N - # tractive effort as pairs of speed and tractive effort - F_T_pairs: # [v in m/s, F_T in N] - F_T_pairs_kmh: # [v in km/h, F_T in N] (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB 642) - - [0.0, 94400] - - [1.0, 94400] - - [2.0, 92800] - - [3.0, 91200] - - [4.0, 89600] - - [5.0, 88000] - - [6.0, 86400] - - [7.0, 84800] - - [8.0, 83200] - - [9.0, 81600] - - [10.0, 80000] - - [11.0, 78160] - - [12.0, 76290] - - [13.0, 74420] - - [14.0, 72550] - - [15.0, 70680] - - [16.0, 68810] - - [17.0, 66940] - - [18.0, 65070] - - [19.0, 63200] - - [20.0, 61330] - - [21.0, 59460] - - [22.0, 57590] - - [23.0, 55720] - - [24.0, 53850] - - [25.0, 51980] - - [26.0, 50110] - - [27.0, 48240] - - [28.0, 46370] - - [29.0, 44500] - - [30.0, 42630] - - [31.0, 40760] - - [32.0, 38890] - - [33.0, 38110] - - [34.0, 37330] - - [35.0, 36550] - - [36.0, 35770] - - [37.0, 35600] - - [38.0, 35600] - - [39.0, 35600] - - [40.0, 35600] - - [41.0, 35600] - - [42.0, 35600] - - [43.0, 35550] - - [44.0, 35280] - - [45.0, 35010] - - [46.0, 34740] - - [47.0, 34280] - - [48.0, 33560] - - [49.0, 32880] - - [50.0, 32220] - - [51.0, 31590] - - [52.0, 26300] - - [53.0, 26300] - - [54.0, 26300] - - [55.0, 26300] - - [56.0, 26300] - - [57.0, 25990] - - [58.0, 25840] - - [59.0, 25690] - - [60.0, 25540] - - [61.0, 25390] - - [62.0, 25240] - - [63.0, 25090] - - [64.0, 25140] - - [65.0, 24760] - - [66.0, 24380] - - [67.0, 24020] - - [68.0, 23660] - - [69.0, 23320] - - [70.0, 22990] - - [71.0, 19400] - - [72.0, 19400] - - [73.0, 19400] - - [74.0, 19400] - - [75.0, 19400] - - [76.0, 19400] - - [77.0, 19400] - - [78.0, 19400] - - [79.0, 19400] - - [80.0, 19400] - - [81.0, 19440] - - [82.0, 19310] - - [83.0, 19180] - - [84.0, 19050] - - [85.0, 18920] - - [86.0, 18670] - - [87.0, 18460] - - [88.0, 18250] - - [89.0, 15360] - - [90.0, 15310] - - [91.0, 15260] - - [92.0, 15210] - - [93.0, 15160] - - [94.0, 15110] - - [95.0, 15060] - - [96.0, 15010] - - [97.0, 14960] - - [98.0, 14910] - - [99.0, 14860] - - [100.0, 14810] - - [101.0, 14760] - - [102.0, 14710] - - [103.0, 14660] - - [104.0, 14610] - - [105.0, 14560] - - [106.0, 14510] - - [107.0, 14460] - - [108.0, 14410] - - [109.0, 14360] - - [110.0, 14310] - - [111.0, 14460] - - [112.0, 14340] - - [113.0, 14210] - - [114.0, 14080] - - [115.0, 13960] - - [116.0, 13840] - - [117.0, 13720] - - [118.0, 13610] - - [119.0, 13490] - - [120.0, 13380] + # tractive effort as pairs of speed and tractive effort + tractive_effort: + - [0.0, 94400] + - [1.0, 94400] + - [2.0, 92800] + - [3.0, 91200] + - [4.0, 89600] + - [5.0, 88000] + - [6.0, 86400] + - [7.0, 84800] + - [8.0, 83200] + - [9.0, 81600] + - [10.0, 80000] + - [11.0, 78160] + - [12.0, 76290] + - [13.0, 74420] + - [14.0, 72550] + - [15.0, 70680] + - [16.0, 68810] + - [17.0, 66940] + - [18.0, 65070] + - [19.0, 63200] + - [20.0, 61330] + - [21.0, 59460] + - [22.0, 57590] + - [23.0, 55720] + - [24.0, 53850] + - [25.0, 51980] + - [26.0, 50110] + - [27.0, 48240] + - [28.0, 46370] + - [29.0, 44500] + - [30.0, 42630] + - [31.0, 40760] + - [32.0, 38890] + - [33.0, 38110] + - [34.0, 37330] + - [35.0, 36550] + - [36.0, 35770] + - [37.0, 35600] + - [38.0, 35600] + - [39.0, 35600] + - [40.0, 35600] + - [41.0, 35600] + - [42.0, 35600] + - [43.0, 35550] + - [44.0, 35280] + - [45.0, 35010] + - [46.0, 34740] + - [47.0, 34280] + - [48.0, 33560] + - [49.0, 32880] + - [50.0, 32220] + - [51.0, 31590] + - [52.0, 26300] + - [53.0, 26300] + - [54.0, 26300] + - [55.0, 26300] + - [56.0, 26300] + - [57.0, 25990] + - [58.0, 25840] + - [59.0, 25690] + - [60.0, 25540] + - [61.0, 25390] + - [62.0, 25240] + - [63.0, 25090] + - [64.0, 25140] + - [65.0, 24760] + - [66.0, 24380] + - [67.0, 24020] + - [68.0, 23660] + - [69.0, 23320] + - [70.0, 22990] + - [71.0, 19400] + - [72.0, 19400] + - [73.0, 19400] + - [74.0, 19400] + - [75.0, 19400] + - [76.0, 19400] + - [77.0, 19400] + - [78.0, 19400] + - [79.0, 19400] + - [80.0, 19400] + - [81.0, 19440] + - [82.0, 19310] + - [83.0, 19180] + - [84.0, 19050] + - [85.0, 18920] + - [86.0, 18670] + - [87.0, 18460] + - [88.0, 18250] + - [89.0, 15360] + - [90.0, 15310] + - [91.0, 15260] + - [92.0, 15210] + - [93.0, 15160] + - [94.0, 15110] + - [95.0, 15060] + - [96.0, 15010] + - [97.0, 14960] + - [98.0, 14910] + - [99.0, 14860] + - [100.0, 14810] + - [101.0, 14760] + - [102.0, 14710] + - [103.0, 14660] + - [104.0, 14610] + - [105.0, 14560] + - [106.0, 14510] + - [107.0, 14460] + - [108.0, 14410] + - [109.0, 14360] + - [110.0, 14310] + - [111.0, 14460] + - [112.0, 14340] + - [113.0, 14210] + - [114.0, 14080] + - [115.0, 13960] + - [116.0, 13840] + - [117.0, 13720] + - [118.0, 13610] + - [119.0, 13490] + - [120.0, 13380] diff --git a/test/data/trains/longdistance.yaml b/test/data/trains/longdistance.yaml index 89ba344..2369208 100644 --- a/test/data/trains/longdistance.yaml +++ b/test/data/trains/longdistance.yaml @@ -1,192 +1,220 @@ %YAML 1.2 --- -train: - name: "Intercity 2 (Traxx P160 AC2 + double deck coaches)" # (source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario#Intercity_2 and https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) - length: 152 # in m (source: FBS: DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) - m_td: 84000 # mass on driving axles of the traction unit in kg (source: FBS: DB146.5) - m_tc: 0 # mass on carrying axles of the traction unit in kg (no carrying axles; source: FBS: DB146.5) - m_w: 309000 # mass of the consist (set of wagons) in kg (source: FBS: 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) - rotationMassFactor_train: - rotationMassFactor_t: 1.09 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit) - rotationMassFactor_w: 1.06 # (source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for freight wagons) - powerType: electric # diesel or electric (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)#Gemeinsame_Merkmale) - type: passenger # "freight" or "passenger" (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) - v_limit: # in m/s - v_limit_kmh: 160 # in km/h (source: https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)#Gemeinsame_Merkmale) - a_braking: -0.3507 # in m/s^2 (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2022.05" +trains: + - name: "Intercity 2 (Traxx P160 AC2 + double deck coaches)" # (source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario#Intercity_2 and https://de.wikipedia.org/wiki/Intercity_2_(Deutsche_Bahn)) + id: IC1011 + formation: [Bombardier_Traxx_2_P160,DABpza68,DABpza68,DABpza68,DABpza68,DABpza668] - # coefficients for the vehicle resistance - # for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_t)/v00)^2) - f_Rtd0: 2.5 # coefficient for basic resistance due to the traction units driving axles (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "Hochgeschwindigkeitslok."" -> 2.0 ‰ to 3.0 ‰) - f_Rtc0: 0 # coefficient for basic resistance due to the traction units carring axles (in ‰) (source: no carrying axles; source: FBS: DB146.5) - F_Rt2: 5000 # coefficient for air resistance of the traction units (in N) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsig, eckige Kopfform"with "Stromabnehmer" -> 5000 N to 6000 N) +vehicles: + - name: "DBpbzfa 668.2" # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + id: DABpza668 + UUID: a186ea2c-daeb-429b-8254-89e0d6700519 + picture: https://commons.wikimedia.org/wiki/File:Bombardier_IC_Doppelstocksteuerwagen_Innotrans_2014.JPG + vehicle_type: passenger # "freight", "passenger", "traction unit" or "multiple unit" - # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_w)/v00)^2)) - f_Rw0: 2.0 # coefficient for the consists basic resistance (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge") - f_Rw1: 0.715 # coefficient for the consists resistance to rolling (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge") - f_Rw2: 3.64 # coefficient fo the consistsr air resistance (in ‰) (source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge") + length: 27.27 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + mass: 58.00 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + load_limit: 20.0 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + speed_limit: 160 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario - # tractive effort as pairs of speed and tractive effort - F_T_pairs: # [v in m/s, F_T in N] - F_T_pairs_kmh: # [v in km/h, F_T in N] (source: FBS -> using the information from the "Fahrschaubild" of a long path without gradient or speed limit for DB146.5 with 1x DApza687.2, 3x DBpza668.2, 1x DBpbzfa668.2) - - [0.0, 300000] - - [1.0, 300000] - - [2.0, 300000] - - [3.0, 300000] - - [4.0, 300000] - - [5.0, 300000] - - [6.0, 300000] - - [7.0, 300000] - - [8.0, 300000] - - [9.0, 300000] - - [10.0, 300000] - - [11.0, 300000] - - [12.0, 300000] - - [13.0, 300000] - - [14.0, 300000] - - [15.0, 300000] - - [16.0, 300000] - - [17.0, 300000] - - [18.0, 300000] - - [19.0, 300000] - - [20.0, 300000] - - [21.0, 300000] - - [22.0, 300000] - - [23.0, 300000] - - [24.0, 300000] - - [25.0, 300000] - - [26.0, 300000] - - [27.0, 300000] - - [28.0, 300000] - - [29.0, 300000] - - [30.0, 300000] - - [31.0, 300000] - - [32.0, 300000] - - [33.0, 300000] - - [34.0, 300000] - - [35.0, 300000] - - [36.0, 300000] - - [37.0, 300000] - - [38.0, 300000] - - [39.0, 300000] - - [40.0, 300000] - - [41.0, 300000] - - [42.0, 300000] - - [43.0, 300000] - - [44.0, 300000] - - [45.0, 300000] - - [46.0, 300000] - - [47.0, 300000] - - [48.0, 300000] - - [49.0, 300000] - - [50.0, 300000] - - [51.0, 300000] - - [52.0, 300000] - - [53.0, 300000] - - [54.0, 300000] - - [55.0, 300000] - - [56.0, 300000] - - [57.0, 300000] - - [58.0, 300000] - - [59.0, 300000] - - [60.0, 300000] - - [61.0, 300000] - - [62.0, 300000] - - [63.0, 300000] - - [64.0, 300000] - - [65.0, 300000] - - [66.0, 300000] - - [67.0, 297760] - - [68.0, 293380] - - [69.0, 289130] - - [70.0, 285000] - - [71.0, 280990] - - [72.0, 277080] - - [73.0, 273290] - - [74.0, 269590] - - [75.0, 266000] - - [76.0, 262500] - - [77.0, 259090] - - [78.0, 255770] - - [79.0, 252530] - - [80.0, 249380] - - [81.0, 246300] - - [82.0, 243290] - - [83.0, 240360] - - [84.0, 237500] - - [85.0, 234710] - - [86.0, 231980] - - [87.0, 229310] - - [88.0, 226700] - - [89.0, 224160] - - [90.0, 221670] - - [91.0, 219230] - - [92.0, 216850] - - [93.0, 214520] - - [94.0, 212230] - - [95.0, 210000] - - [96.0, 207810] - - [97.0, 205670] - - [98.0, 203570] - - [99.0, 201520] - - [100.0, 199500] - - [101.0, 197520] - - [102.0, 195590] - - [103.0, 193690] - - [104.0, 191830] - - [105.0, 190000] - - [106.0, 188210] - - [107.0, 186450] - - [108.0, 184720] - - [109.0, 183030] - - [110.0, 181360] - - [111.0, 179730] - - [112.0, 178130] - - [113.0, 176550] - - [114.0, 175000] - - [115.0, 173480] - - [116.0, 171980] - - [117.0, 170510] - - [118.0, 169070] - - [119.0, 167650] - - [120.0, 166250] - - [121.0, 164880] - - [122.0, 163520] - - [123.0, 162200] - - [124.0, 160890] - - [125.0, 159600] - - [126.0, 158330] - - [127.0, 157090] - - [128.0, 155860] - - [129.0, 154650] - - [130.0, 153460] - - [131.0, 152290] - - [132.0, 151140] - - [133.0, 150000] - - [134.0, 148880] - - [135.0, 147780] - - [136.0, 146690] - - [137.0, 145620] - - [138.0, 144570] - - [139.0, 143530] - - [140.0, 142500] - - [141.0, 141490] - - [142.0, 140490] - - [143.0, 139510] - - [144.0, 138540] - - [145.0, 137590] - - [146.0, 136640] - - [147.0, 135710] - - [148.0, 134800] - - [149.0, 133890] - - [150.0, 133000] - - [151.0, 132120] - - [152.0, 131250] - - [153.0, 130390] - - [154.0, 129550] - - [155.0, 128710] - - [156.0, 127880] - - [157.0, 127070] - - [158.0, 126270] - - [159.0, 125470] - - [160.0, 124690] + rotation_mass: 1.06 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for freight wagons + base_resistance: 2.0 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + rolling_resistance: 0.715 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + air_resistance: 3.64 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + + - name: "DApza 687.2 and DBpza 682.2" # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + id: DABpza68 + UUID: 45e83b27-7952-4f34-ad8d-1affcacfe6f2 + picture: https://commons.wikimedia.org/wiki/File:D-DB_50_80_26-81_456-8_DBpza_682.2_Bremen_Hbf_14.11.2015.jpg + vehicle_type: passenger # "freight", "passenger", "traction unit" or "multiple unit" + + length: 26.8 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + mass: 50.00 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + load_limit: 20.0 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + speed_limit: 160 # source: https://de.wikipedia.org/wiki/Bombardier_Twindexx_Vario + + rotation_mass: 1.06 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for freight wagons + base_resistance: 2.0 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + rolling_resistance: 0.715 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + air_resistance: 3.64 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 153 for "Doppelstockgliederzüge" + + - name: "Bombardier Traxx 2 (P160)" # source: https://en.wikipedia.org/wiki/Bombardier_TRAXX#TRAXX_P160_AC2 + id: Bombardier_Traxx_2_P160 + UUID: bc62a03e-9ec9-41f2-913c-6b315e86cc61 + picture: https://commons.wikimedia.org/wiki/File:146_560-8_(Flickr_26502799445).jpg + vehicle_type: traction unit # "freight", "passenger", "traction unit" or "multiple unit" + power_type: electric # "diesel", "electric", or "steam" + + length: 18.9 # source: https://de.wikipedia.org/wiki/Bombardier_Traxx#P160_AC2 + mass: 85 # source: https://de.wikipedia.org/wiki/Bombardier_Traxx#P160_AC2 + mass_traction: 85 # in t # mass on driving axles of the traction unit + speed_limit: 160 # in km/h + + rotation_mass: 1.09 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit + base_resistance: 2.5 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsige Diesellokomot." -> 2.2 ‰ to 3.5 ‰ + air_resistance: 0.006 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsig, eckige Kopfform" with "Stromabnehmer" -> 5000 N to 6000 N; modified for the used formula + + tractive_effort: + - [0.0, 300000] + - [1.0, 300000] + - [2.0, 300000] + - [3.0, 300000] + - [4.0, 300000] + - [5.0, 300000] + - [6.0, 300000] + - [7.0, 300000] + - [8.0, 300000] + - [9.0, 300000] + - [10.0, 300000] + - [11.0, 300000] + - [12.0, 300000] + - [13.0, 300000] + - [14.0, 300000] + - [15.0, 300000] + - [16.0, 300000] + - [17.0, 300000] + - [18.0, 300000] + - [19.0, 300000] + - [20.0, 300000] + - [21.0, 300000] + - [22.0, 300000] + - [23.0, 300000] + - [24.0, 300000] + - [25.0, 300000] + - [26.0, 300000] + - [27.0, 300000] + - [28.0, 300000] + - [29.0, 300000] + - [30.0, 300000] + - [31.0, 300000] + - [32.0, 300000] + - [33.0, 300000] + - [34.0, 300000] + - [35.0, 300000] + - [36.0, 300000] + - [37.0, 300000] + - [38.0, 300000] + - [39.0, 300000] + - [40.0, 300000] + - [41.0, 300000] + - [42.0, 300000] + - [43.0, 300000] + - [44.0, 300000] + - [45.0, 300000] + - [46.0, 300000] + - [47.0, 300000] + - [48.0, 300000] + - [49.0, 300000] + - [50.0, 300000] + - [51.0, 300000] + - [52.0, 300000] + - [53.0, 300000] + - [54.0, 300000] + - [55.0, 300000] + - [56.0, 300000] + - [57.0, 300000] + - [58.0, 300000] + - [59.0, 300000] + - [60.0, 300000] + - [61.0, 300000] + - [62.0, 300000] + - [63.0, 300000] + - [64.0, 300000] + - [65.0, 300000] + - [66.0, 300000] + - [67.0, 297760] + - [68.0, 293380] + - [69.0, 289130] + - [70.0, 285000] + - [71.0, 280990] + - [72.0, 277080] + - [73.0, 273290] + - [74.0, 269590] + - [75.0, 266000] + - [76.0, 262500] + - [77.0, 259090] + - [78.0, 255770] + - [79.0, 252530] + - [80.0, 249380] + - [81.0, 246300] + - [82.0, 243290] + - [83.0, 240360] + - [84.0, 237500] + - [85.0, 234710] + - [86.0, 231980] + - [87.0, 229310] + - [88.0, 226700] + - [89.0, 224160] + - [90.0, 221670] + - [91.0, 219230] + - [92.0, 216850] + - [93.0, 214520] + - [94.0, 212230] + - [95.0, 210000] + - [96.0, 207810] + - [97.0, 205670] + - [98.0, 203570] + - [99.0, 201520] + - [100.0, 199500] + - [101.0, 197520] + - [102.0, 195590] + - [103.0, 193690] + - [104.0, 191830] + - [105.0, 190000] + - [106.0, 188210] + - [107.0, 186450] + - [108.0, 184720] + - [109.0, 183030] + - [110.0, 181360] + - [111.0, 179730] + - [112.0, 178130] + - [113.0, 176550] + - [114.0, 175000] + - [115.0, 173480] + - [116.0, 171980] + - [117.0, 170510] + - [118.0, 169070] + - [119.0, 167650] + - [120.0, 166250] + - [121.0, 164880] + - [122.0, 163520] + - [123.0, 162200] + - [124.0, 160890] + - [125.0, 159600] + - [126.0, 158330] + - [127.0, 157090] + - [128.0, 155860] + - [129.0, 154650] + - [130.0, 153460] + - [131.0, 152290] + - [132.0, 151140] + - [133.0, 150000] + - [134.0, 148880] + - [135.0, 147780] + - [136.0, 146690] + - [137.0, 145620] + - [138.0, 144570] + - [139.0, 143530] + - [140.0, 142500] + - [141.0, 141490] + - [142.0, 140490] + - [143.0, 139510] + - [144.0, 138540] + - [145.0, 137590] + - [146.0, 136640] + - [147.0, 135710] + - [148.0, 134800] + - [149.0, 133890] + - [150.0, 133000] + - [151.0, 132120] + - [152.0, 131250] + - [153.0, 130390] + - [154.0, 129550] + - [155.0, 128710] + - [156.0, 127880] + - [157.0, 127070] + - [158.0, 126270] + - [159.0, 125470] + - [160.0, 124690] From a5868af2c5633972cb8e666e5cd9c61b67c5992a Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 12 May 2022 16:32:15 +0200 Subject: [PATCH 14/18] new type Train as struct --- CHANGELOG.md | 4 +- Project.toml | 1 + src/TrainRuns.jl | 24 +- src/behavior.jl | 107 +++--- src/calc.jl | 10 +- src/characteristics.jl | 14 +- src/constructors.jl | 349 +++++++++++++++++++- src/formulary.jl | 37 +-- src/import.jl | 35 -- src/output.jl | 4 +- src/types.jl | 717 ++--------------------------------------- test/runtests.jl | 8 +- 12 files changed, 478 insertions(+), 832 deletions(-) delete mode 100644 src/import.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 992cad2..f0281f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * renamed TrainRun into TrainRuns * replaced settings::Dict with type Settings as struct * replaced path::Dict with type Path as struct +* replaced train::Dict with type Train as struct * restructured examples/ and data/ in docs/ and test/ * modified test to work with Julia Testsets and with simplier naming of input files * renamed Validate.jl into types.jl @@ -28,13 +29,14 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * moved createCharacteristicSection() from characteristics.jl to types.jl * changed title of include files from upper case to lower case * changed seperation of submodules into a single module with file include -* updated test files to railtoolkit/schema (2022.04) +* updated test files to railtoolkit/schema (2022.05) ### Removed * dependency Plots * AdditionalOutput.jl * EnergySaving.jl * test/testEnums.jl +* import.jl ## Version [0.8] 2022-01-20 diff --git a/Project.toml b/Project.toml index d09852c..1fbfcb9 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" diff --git a/src/TrainRuns.jl b/src/TrainRuns.jl index e511c52..54997db 100644 --- a/src/TrainRuns.jl +++ b/src/TrainRuns.jl @@ -9,28 +9,32 @@ __precompile__(true) module TrainRuns ## loading standard library packages -using UUIDs, Dates +using UUIDs, Dates, Statistics ## loading external packages using YAML, JSONSchema, CSV, DataFrames export ## Interface -trainrun, Path, Settings, exportToCsv +trainrun, Train, Path, Settings, exportToCsv + +## global variables +global g = 9.80665 # acceleration due to gravity (in m/s^2) +global μ = 0.2 # friction as constant, todo: implement as function +global Δv_air = 15.0/3.6 # coefficient for velocitiy difference between train and outdoor air (in m/s) ## include package files include("types.jl") include("constructors.jl") include("formulary.jl") +include("calc.jl") include("characteristics.jl") include("behavior.jl") include("output.jl") -include("import.jl") include("export.jl") -include("calc.jl") ## main function """ - trainrun(train::Dict, path::Path, settings::Settings) + trainrun(train::Train, path::Path, settings::Settings) Calculate the running time of a `train` on a `path`. The `settings` provides the choice of models for the calculation. @@ -43,15 +47,7 @@ julia> trainrun(train, path) xxx.xx # in seconds ``` """ -function trainrun(trainInput::Dict, path::Path, settings=Settings()::Settings) - # copy Input data for not changing them - # TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && isempty(path.poi) - train = copy(trainInput) - - # check the input data - train = checkAndSetTrain!(train) - settings.outputDetail == :everything && println("The input has been checked.") - +function trainrun(train::Train, path::Path, settings=Settings()::Settings) # prepare the input data movingSection = determineCharacteristics(path, train, settings) settings.outputDetail == :everything && println("The moving section has been prepared.") diff --git a/src/behavior.jl b/src/behavior.jl index ef03b07..7ddae8c 100644 --- a/src/behavior.jl +++ b/src/behavior.jl @@ -5,7 +5,6 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -## functions for calculating tractive effort and resisting forces """ calculateTractiveEffort(v, tractiveEffortVelocityPairs) @@ -14,19 +13,19 @@ Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` depe ... # Arguments - `v::AbstractFloat`: the current velocity in m/s. -- `tractiveEffortVelocityPairs::Array{Array{AbstractFloat,1},1}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. +- `tractiveEffortVelocityPairs::Array{}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. ... # Examples ```julia-repl -julia> calculateTractiveEffort(20.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) +julia> calculateTractiveEffort(20.0, [(0.0, 180000), (20.0, 100000), (40.0, 60000), (60.0, 40000), (80.0, 30000)]) 100000 -julia> calculateTractiveEffort(30.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) +julia> calculateTractiveEffort(30.0, [(0.0, 180000), (20.0, 100000), (40.0, 60000), (60.0, 40000), (80.0, 30000)]) 80000 ``` """ -function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs) +function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs::Array{}) for row in 1:length(tractiveEffortVelocityPairs) nextPair = tractiveEffortVelocityPairs[row] if nextPair[1] == v @@ -46,19 +45,19 @@ end #function calculateTractiveEffort """ calculate and return the path resistance dependend on the trains position and mass model """ -function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) +function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Train) if massModel == :mass_point - pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) + pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full) elseif massModel == :homogeneous_strip pathResistance = 0.0 - s_rear = s - train[:length] # position of the rear of the train + s_rear = s - train.length # position of the rear of the train while csId > 0 && s_rear < CSs[csId][:s_exit] - pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train[:length] * calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) + pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train.length * calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full) csId = csId-1 if csId == 0 # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used - return pathResistance + (CSs[1][:s_entry] - s_rear) / train[:length] * calcForceFromCoefficient(CSs[1][:r_path], train[:m_train]) + return pathResistance + (CSs[1][:s_entry] - s_rear) / train.length * calcForceFromCoefficient(CSs[1][:r_path], train.m_train_full) end #if end #while end #if @@ -69,7 +68,7 @@ end #function calculatePathResistance """ calculate and return tractive and resisting forces for a data point """ -function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Dict, massModel) +function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Train, massModel) # calculate resisting forces dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) @@ -81,9 +80,9 @@ function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bs if bsType == "braking" || bsType == "coasting" dataPoint[:F_T] = 0.0 elseif bsType == "cruising" - dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])) + dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train.tractiveEffort)) else # bsType == "accelerating" || bsType == "diminishing" || 'default' - dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs]) + dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort) end return dataPoint @@ -191,7 +190,7 @@ end #function getNextPointOfInterest ## This function calculates the data points of the breakFree section. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. # Info: currently the values of the breakFree section will be calculated like in the accelerating section -function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) # conditions for the break free section endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] trainIsHalting = drivingCourse[end][:v] == 0.0 @@ -243,7 +242,7 @@ function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags: if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] s_braking = 0.0 else - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end # reset state flags @@ -260,16 +259,16 @@ end #function addBreakFreeSection! ## This function calculates the data points of the clearing section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. -function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) if stateFlags[:previousSpeedLimitReached] - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] ignoreBraking = true s_braking = 0.0 else ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end s_clearing = min(CS[:s_exit]-drivingCourse[end][:s]-s_braking, currentSpeedLimit[:s_end] - drivingCourse[end][:s]) @@ -282,7 +281,7 @@ function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id]) end #stateFlags[:previousSpeedLimitReached] = false - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] else stateFlags[:error] = true @@ -293,9 +292,9 @@ end #function addClearingSection ## This function calculates the data points of the accelerating section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section -function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) - #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) +function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) + #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Train, CSs::Vector{Dict}, ignoreBraking::Bool) + #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Train) CSs = movingSection[:characteristicSections] CS = CSs[csId] drivingCourse = movingSection[:drivingCourse]=# @@ -307,7 +306,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla s_braking = 0.0 else ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end # conditions for the accelerating section @@ -322,7 +321,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) drivingCourse[end][:behavior] = BS[:type] - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit] #speedLimitReached = drivingCourse[end][:v] > currentSpeedLimit[:v] @@ -334,18 +333,18 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) end # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) + drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train) # create the next data point push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) @@ -356,7 +355,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla # conditions for the next while cycle if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] speedLimitReached = drivingCourse[end][:v] > CS[:v_limit] @@ -539,7 +538,7 @@ end #function addAcceleratingSection! ## This function calculates the data points of the cruising section. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed. -function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Dict, CSs::Vector{Dict}, cruisingType::String) +function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Train, CSs::Vector{Dict}, cruisingType::String) trainIsClearing = cruisingType == "clearing" trainIsBrakingDownhill = cruisingType == "downhillBraking" @@ -555,11 +554,11 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: s_braking = 0.0 else ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end # conditions for cruising section - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] @@ -584,7 +583,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: if settings.massModel == :homogeneous_strip && CS[:id] > 1 # conditions for cruising section - trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] + trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train.length targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising resistingForceNegative = drivingCourse[end][:F_R] < 0.0 # targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak] @@ -598,7 +597,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] + # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train.length && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] # the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS #TODO: maybe just consider former CS with different path resistance? # tractive effort (in N): @@ -617,7 +616,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: if settings.stepVariable == :distance || settings.stepVariable == time push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) else - push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? + push!(drivingCourse, moveAStep(drivingCourse[end], position, train.length/(10.0^cycle), CS[:id])) # TODO which step size should be used? end drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) @@ -635,7 +634,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] + trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train.length resistingForceNegative = drivingCourse[end][:F_R] < 0.0 end #while @@ -657,7 +656,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: currentStepSize = settings.stepSize / 10.0^cycle end - elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train[:length])) + elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train.length)) if settings.stepVariable == :distance currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] else @@ -667,7 +666,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] break - elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] + elseif drivingCourse[end][:s] >= CS[:s_entry] + train.length break elseif drivingCourse[end][:s] == nextPointOfInterest[1] @@ -782,12 +781,12 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: # set state flags stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit] if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit] stateFlags[:tractionDeficit] = tractionDeficit stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0 - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative))) @@ -796,7 +795,7 @@ end #function addCruisingSection! ## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower -function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] @@ -804,14 +803,14 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag s_braking = 0.0 else ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end # conditions for diminishing section targetSpeedReached = drivingCourse[end][:v] <= 0.0 endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit] - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] # use the conditions for the diminishing section @@ -828,7 +827,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached # 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) + drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train) # create the next data point push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) @@ -839,7 +838,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag # conditions for the next while cycle if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) end brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] @@ -983,7 +982,7 @@ end #function addDiminishingSection! ## This function calculates the data points of the coasting section. # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section -function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) # TODO: if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept # with getCurrentSpeedLimit @@ -991,7 +990,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] # use the conditions for the coasting section @@ -1011,7 +1010,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) + drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train.m_train_full, train.ξ_train) # create the next data point push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) @@ -1019,7 +1018,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: push!(BS[:dataPoints], drivingCourse[end][:i]) # conditions for the next while cycle - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] @@ -1146,7 +1145,7 @@ end #function addCoastingSection! ## This function calculates the data points of the braking section. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. -function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Train, CSs::Vector{Dict}) # conditions for braking section targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] @@ -1168,7 +1167,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) # acceleration (in m/s^2): - drivingCourse[end][:a] = train[:a_braking] + drivingCourse[end][:a] = train.a_braking # TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s]) if settings.stepVariable == :distance && ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 @@ -1289,7 +1288,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D end # else: return the characteristic section without a braking section # set state flags - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) + currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train.length) stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] stateFlags[:endOfCSReached] = endOfCSReached @@ -1303,7 +1302,7 @@ end #function addBrakingSection! ## This function calculates the data point of the standstill. # Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. -function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) +function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Train, CSs::Vector{Dict}) if drivingCourse[end][:v] == 0.0 BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) merge!(BS, Dict(:length => 0.0, # total length (in m) @@ -1347,8 +1346,8 @@ function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) # calculate other values previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs]) # # TODO: just for testing -# if previousPoint[:a]=0.0 -# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train[:a_braking]) +# if previousPoint[:a]=0.0 +# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train.a_braking) # end currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s) currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s) diff --git a/src/calc.jl b/src/calc.jl index 879f3d2..e9ed5ef 100644 --- a/src/calc.jl +++ b/src/calc.jl @@ -8,7 +8,7 @@ # Calculate the running time of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. # calculate a train run focussing on using the minimum possible running time -function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) +function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Train) CSs::Vector{Dict} = movingSection[:characteristicSections] if settings.massModel == :homogeneous_strip && settings.stepVariable == speed @@ -32,7 +32,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t end # determine the different flags for switching between the states for creatinge moving phases - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N) previousSpeedLimitReached = false @@ -64,12 +64,12 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t elseif settings.stepVariable == time s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v]) elseif settings.stepVariable == velocity - s_cruising = train[:length]/(10.0) # TODO which step size should be used? + s_cruising = train.length/(10.0) # TODO which step size should be used? end (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking if s_cruising > 0.0 @@ -79,7 +79,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t end elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) + s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking) s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking if s_cruising > 0.0 # TODO: define a minimum cruising length? diff --git a/src/characteristics.jl b/src/characteristics.jl index f0e3baf..7abcd10 100644 --- a/src/characteristics.jl +++ b/src/characteristics.jl @@ -6,9 +6,9 @@ # __license__ = "ISC" ## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior -function determineCharacteristics(path::Path, train::Dict, settings::Settings) - movingSection = createMovingSection(path, train[:v_limit], train[:length]) - movingSection = secureBrakingBehavior!(movingSection, train[:a_braking]) +function determineCharacteristics(path::Path, train::Train, settings::Settings) + movingSection = createMovingSection(path, train.v_limit, train.length) + movingSection = secureBrakingBehavior!(movingSection, train.a_braking) movingSection = secureAcceleratingBehavior!(movingSection, settings, train) #movingSection = secureCruisingBehavior!(movingSection, settings, train) @@ -45,7 +45,7 @@ function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) end #function secureBrakingBehavior! ## define the intersection velocities between the characterisitc sections to secure accelerating behavior -function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Dict) +function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Train) # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards CSs = movingSection[:characteristicSections] @@ -82,7 +82,7 @@ function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, tr (CS, acceleratingCourse, stateFlags) = addClearingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the train is not allowed to accelerate because of a previous speed limit end else - if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train[:length] + if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train.length break else (CS, acceleratingCourse, stateFlags) = addDiminishingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort @@ -112,7 +112,7 @@ end #function secureAcceleratingBehavior! #= ## define the intersection velocities between the characterisitc sections to secure cruising behavior -function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Dict) +function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Train) # limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak CSs = movingSection[:characteristicSections] @@ -147,7 +147,7 @@ function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train: (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") end else - if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train[:length] + if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train.length break else (CS, cruisingCourse, stateFlags) = addDiminishingSection!(CS, cruisingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort diff --git a/src/constructors.jl b/src/constructors.jl index f795ed4..d6d156f 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -1,7 +1,7 @@ #!/usr/bin/env julia # -*- coding: UTF-8 -*- # __julia-version__ = 1.7.2 -# __author__ = "Martin Scheidt" +# __author__ = "Martin Scheidt, Max Kannenberg" # __copyright__ = "2022" # __license__ = "ISC" @@ -77,7 +77,7 @@ function Settings(file="DEFAULT") settings = Dict() end - ## set the variables if they exist in "settings" + ## set the variables in "settings" haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing @@ -89,8 +89,7 @@ function Settings(file="DEFAULT") Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) -end #function Settings() # constructor - +end #function Settings() # outer constructor """ Path(file, type = :YAML) @@ -227,7 +226,7 @@ function Path(file, type = :YAML) end path = paths[1] - ## set the variables if they exist in "settings" + ## set the variables in "path" # required name = path["name"] id = path["id"] @@ -275,7 +274,345 @@ function Path(file, type = :YAML) Path(name, id, uuid, poi, sections) -end #function Path() # constructor +end #function Path() # outer constructor + +""" + Train(file, type = :YAML) + +Train is a datastruture for calculation context. +The function Train() will create a train to use in calculations. +Supported formats for the YAML files are: railtoolkit/schema (2022.05) + +# Example +```jldoctest +julia> my_train = Train("file.yaml") # will generate a train from a YAML file. +Train(variables) +``` +""" +function Train(file, type = :YAML) + + ## default values + name = "" # + id = "" # + uuid = UUIDs.uuid4() # + length = 0 # in meter + m_train_full = 0 # in kilogram + m_train_empty = 0 # in kilogram + m_loco = 0 # in kilogram + m_td = 0 # in kilogram + m_tc = 0 # in kilogram + m_w = 0 # in kilogram + ξ_train = 1.08 # rotation mass factor, source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Zug, überschlägliche Berechnung" + ξ_loco = 1.09 # rotation mass factor + ξ_cars = 1.06 # rotation mass factor + transportType = :freight # "freight" or "passenger" for resistance calculation + v_limit = 140 # in m/s (default 504 km/h) + a_braking = 0 # in m/s^2, todo: implement as function + f_Rtd0 = 0 # coefficient for basic resistance due to the traction units driving axles (in ‰) + f_Rtc0 = 0 # coefficient for basic resistance due to the traction units carring axles (in ‰) + F_Rt2 = 3000 # coefficient for air resistance of the traction units (in N) + f_Rw0 = 0 # coefficient for the consists basic resistance (in ‰) + f_Rw1 = 0 # coefficient for the consists resistance to rolling (in ‰) + f_Rw2 = 0 # coefficient fo the consistsr air resistance (in ‰) + F_v_pairs = [] # [v in m/s, F_T in N] + + ## load from file + if type == :YAML + + data = YAML.load(open(file)) + if data["schema"] != "https://railtoolkit.org/schema/rolling-stock.json" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/rolling-stock (2022.05)") + end + if data["schema_version"] != "2022.05" + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/rolling-stock (2022.05)") + end + + ## JSON schema for YAML-file validation + railtoolkit_schema = Schema("""{ + "required": [ "schema", "schema_version" ], + "anyOf": [ + {"required": [ "trains" ] }, + {"required": [ "vehicles" ] } + ], + "properties": { + "schema": { + "description": "Identifier of the schema", + "enum": [ "https://railtoolkit.org/schema/rolling-stock.json" ] + }, + "schema_version": { + "description": "Version of the schema", + "type": "string", + "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" + }, + "trains": { + "type": "array", + "minItems": 1, + "items": { + "required": [ "name", "id", "formation" ], + "type": "object", + "properties": { + "id": { + "description": "Identifier of the train", + "type": "string" + }, + "name": { + "description": "Name of the train", + "type": "string" + }, + "UUID": { + "description": "The unique identifier for a train", + "type": "string", + "format": "uuid" + }, + "formation": { + "description": "Collection of vehicles that form the train", + "type": "array", + "minItems": 1, + "uniqueItems": false, + "items": { + "type": "string" + } + } + } + } + }, + "vehicles": { + "type": "array", + "minItems": 1, + "items": { + "required": [ "name", "id", "vehicle_type", "length", "mass" ], + "type": "object", + "properties": { + "air_resistance": { + "description": "coefficient for air resistance in permil", + "type": "number", + "exclusiveMinimum": 0 + }, + "base_resistance": { + "description": "coefficient for basic resistance in permil", + "type": "number", + "exclusiveMinimum": 0 + }, + "id": { + "description": "Identifier of the vehicle", + "type": "string" + }, + "length": { + "description": "The length of the vehicle in meter", + "type": "number", + "exclusiveMinimum": 0 + }, + "load_limit": { + "description": "The maximum permitted load of the vehicle in metric ton", + "type": "number", + "exclusiveMinimum": 0 + }, + "mass_traction": { + "description": "The mass on the powered axles of the vehicle in metric ton", + "type": "number", + "exclusiveMinimum": 0 + }, + "mass": { + "description": "The empty mass of the vehicle in metric ton", + "type": "number", + "exclusiveMinimum": 0 + }, + "name": { + "description": "Name of the vehicle", + "type": "string" + }, + "picture": { + "description": "A URI with a picture for humans", + "type": "string", + "format": "uri" + }, + "power_type": { + "description": "Type of propulsion", + "enum": [ "diesel", "electric", "steam" ] + }, + "rolling_resistance": { + "description": "coefficient for resistance of rolling axles in permil", + "type": "number", + "exclusiveMinimum": 0 + }, + "rotation_mass": { + "description": "Factor for rotating mass; >= 1", + "type": "number", + "minimum": 1 + }, + "speed_limit": { + "description": "Maximum permitted speed in kilometers per hour", + "type": "number", + "exclusiveMinimum": 0 + }, + "tractive_effort": { + "description": "Tractive effort as pairs of speed in kilometers per hour and tractive force in newton", + "type": "array", + "minItems": 3, + "uniqueItems": true, + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "number", + "minimum": 0 + } + } + }, + "UUID": { + "description": "The unique identifier for a vehicle", + "type": "string", + "format": "uuid" + }, + "vehicle_type": { + "description": "Type of vehicle", + "enum": [ "traction unit", "freight", "passenger", "multiple unit" ] + } + } + } + } + } + }""") + + try + validate(railtoolkit_schema, data) + catch err + error("Could not load path file '$file'.\n + YAML format is not recognized. + Currently supported: railtoolkit/schema/rolling-stock (2022.05)") + end + + else + error("Unknown file type '$type'") + end #if type + + trains = data["trains"] + Base.length(trains) > 1 ? println("WARNING: the loaded file contains more than one train. Using only the first!") : nothing + Base.length(trains) == 0 ? error("No train present in file '$file'") : nothing + train = trains[1] + used_vehicles = unique(train["formation"]) + + included_vehicles = [] + for vehicle in data["vehicles"] + push!(included_vehicles,vehicle["id"]) + end + + ## test if all vehicles of the formation are avilable + for vehicle in used_vehicles + vehicle ∉ included_vehicles ? error("'$vehicle' is not present in '$file'") : nothing + end + + ## gather the count of vehicles and usage in the formation + vehicles = NamedTuple[] + for vehicle in data["vehicles"] + if vehicle["id"] in used_vehicles + n = count(==(vehicle["id"]),train["formation"]) + type = vehicle["vehicle_type"] + type == "traction unit" || type == "multiple unit" ? propulsion = true : propulsion = false + type == "passenger" || type == "multiple unit" ? transportType = :passenger : nothing + push!(vehicles, (data=vehicle, n=n, propulsion=propulsion) ) + end + end + + ## set the variables in "train" + name = train["name"] + id = train["id"] + haskey(train, "UUID") ? uuid = parse(UUID, train["UUID"] ) : nothing + transportType == :freight ? a_braking = -0.225 : a_braking = -0.375 # set a default a_braking value depending on the train type + + ## set the variables for all vehicles + for vehicle in vehicles + length += vehicle.data["length"] * vehicle.n + m_train_full += vehicle.data["mass"] * vehicle.n * 1000 # in kg + m_train_empty += vehicle.data["mass"] * vehicle.n * 1000 # in kg + haskey(vehicle.data, "load_limit") ? + m_train_full += vehicle.data["load_limit"] * vehicle.n * 1000 : # in kg + nothing + haskey(vehicle.data, "speed_limit") ? + v_limit > vehicle.data["speed_limit"]/3.6 ? v_limit = vehicle.data["speed_limit"]/3.6 : nothing : + nothing + end + + ## divide vehicles in propulsion and non-propulsion + loco = [] + for i in 1:Base.length(vehicles) + if vehicles[i].propulsion + push!(loco, vehicles[i]) + deleteat!(vehicles, i) + end + end + Base.length(loco) > 1 ? println("WARNING: the loaded file contains more than one traction unit or multiple unit. Using only the first!") : nothing + loco[1].n > 1 ? println("WARNING: the loaded file contains more than one traction unit or multiple unit. Using only one!") : nothing + Base.length(loco) == 0 ? error("No traction unit or multiple unit present in file '$file'") : nothing + loco = loco[1].data + cars = vehicles + + ## set the variables for locos + m_loco= loco["mass"] * 1000 + haskey(loco, "a_braking") ? a_braking = loco["a_braking"] : nothing + haskey(loco, "base_resistance") ? f_Rtd0 = loco["base_resistance"] : nothing + haskey(loco, "rolling_resistance") ? f_Rtc0 = loco["rolling_resistance"] : nothing + haskey(loco, "air_resistance") ? F_Rt2 = loco["air_resistance"] * g * m_loco : nothing + haskey(loco, "mass_traction") ? m_td = loco["mass_traction"] * 1000 : m_td = m_t + haskey(loco, "rotation_mass") ? ξ_loco = loco["rotation_mass"] : nothing + m_tc = m_loco- m_td + haskey(loco, "tractive_effort") ? F_v_pairs = loco["tractive_effort"] : F_v_pairs = [ [0.0, m_td * g * μ],[v_limit*3.6, m_td * g * μ] ] + F_v_pairs = reduce(hcat,F_v_pairs)' # convert to matrix + F_v_pairs[:,1] ./= 3.6 # convert km/h to m/s + F_v_pairs = tuple.(eachcol(F_v_pairs)...) # convert each row to tuples + + ## set the variables for cars + if !isempty(cars) + resis_base = [] + resis_roll = [] + resis_air = [] + rotMassFac = [] + for car in cars + haskey(car.data, "base_resistance") ? + append!(resis_base,repeat([car.data["base_resistance"]],car.n)) : + append!(resis_base,repeat([f_Rw0],car.n)) + haskey(car.data, "rolling_resistance") ? + append!(resis_roll,repeat([car.data["rolling_resistance"]],car.n)) : + append!(resis_roll,repeat([f_Rw1],car.n)) + haskey(car.data, "air_resistance") ? + append!(resis_air,repeat([car.data["air_resistance"]],car.n)) : + append!(resis_air, repeat([f_Rw2],car.n)) + haskey(car.data, "rotation_mass") ? + append!(rotMassFac,repeat([(car.data["rotation_mass"],car.data["mass"])],car.n)) : + append!(rotMassFac,repeat([(ξ_cars ,car.data["mass"])],car.n)) + m_w += car.data["mass"] * car.n * 1000 # in kg + end + f_Rw0 = Statistics.mean(resis_base) + f_Rw1 = Statistics.mean(resis_roll) + f_Rw2 = Statistics.mean(resis_air) + carRotMass = 0 + for elem in rotMassFac + carRotMass += elem[1]*elem[2] * 1000 # in kg + end + ξ_cars = carRotMass/m_w + ξ_train = (ξ_loco * m_loco+ carRotMass)/m_train_empty + else + ξ_cars = 0 + ξ_train = ξ_loco + end + + Train( + name, id, uuid, length, + m_train_full, m_td, m_tc, m_w, + ξ_train, ξ_loco, ξ_cars, + transportType, v_limit, + a_braking, + f_Rtd0, f_Rtc0, F_Rt2, f_Rw0, f_Rw1, f_Rw2, + F_v_pairs + ) + +end #function Train() # outer constructor ## create a moving section containing characteristic sections function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) diff --git a/src/formulary.jl b/src/formulary.jl index 1be57a6..4bf6125 100644 --- a/src/formulary.jl +++ b/src/formulary.jl @@ -30,7 +30,6 @@ approxLevel = 6 v00 = 100/3.6 # velocity factor (in m/s) -g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2 ## calculate forces @@ -43,7 +42,7 @@ Calculate the vehicle resistance for the traction unit of the `train` dependend ... # Arguments - `v::AbstractFloat`: the current velocity in m/s. -- `train::Dict`: ? ? ? +- `train::Train`: ? ? ? ... # Examples @@ -52,36 +51,34 @@ julia> calcTractionUnitResistance(30.0, ? ? ?) ? ? ? ``` """ -function calcTractionUnitResistance(v::AbstractFloat, train::Dict) +function calcTractionUnitResistance(v::AbstractFloat, train::Train) # equation is based on [Wende:2003, page 151] - f_Rtd0 = train[:f_Rtd0] # coefficient for basic resistance due to the traction units driving axles (in ‰) - f_Rtc0 = train[:f_Rtc0] # coefficient for basic resistance due to the traction units carring axles (in ‰) - F_Rt2 = train[:F_Rt2] # coefficient for air resistance of the traction units (in N) - m_td = train[:m_td] # mass on the traction unit's driving axles (in kg) - m_tc = train[:m_tc] # mass on the traction unit's carrying axles (in kg) - Δv_t = train[:Δv_t] # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) + f_Rtd0 = train.f_Rtd0 # coefficient for basic resistance due to the traction units driving axles (in ‰) + f_Rtc0 = train.f_Rtc0 # coefficient for basic resistance due to the traction units carring axles (in ‰) + F_Rt2 = train.F_Rt2 # coefficient for air resistance of the traction units (in N) + m_td = train.m_td # mass on the traction unit's driving axles (in kg) + m_tc = train.m_tc # mass on the traction unit's carrying axles (in kg) - F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ - # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) + F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_air) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ + # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_air) /v00)^2 # vehicle resistance of the traction unit (in N) return F_R_tractionUnit #TODO: same variable name like in the rest of the tool? return R_traction - #TODO: just one line? return train[:f_Rtd0]/1000*train[:m_td]*g+train[:f_Rtc0]/1000*train[:m_tc]*g+train[:F_Rt2]*((v+train[:Δv_t])/v00)^2 # /1000 because of the unit ‰ + #TODO: just one line? return train.f_Rtd0/1000*train.m_td*g+train.f_Rtc0/1000*train.m_tc*g+train.F_Rt2*((v+train.Δv_air)/v00)^2 # /1000 because of the unit ‰ end #function calcTractionUnitResistance """ TODO calculate and return the wagons vehicle resistance dependend on the velocity """ -function calcWagonsResistance(v::AbstractFloat, train::Dict) +function calcWagonsResistance(v::AbstractFloat, train::Train) # equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135) - f_Rw0 = train[:f_Rw0] # coefficient for basic resistance of the set of wagons (consist) (in ‰) - f_Rw1 = train[:f_Rw1] # coefficient for the consists resistance to rolling (in ‰) - f_Rw2 = train[:f_Rw2] # coefficient fo the consistsr air resistance (in ‰) - m_w = train[:m_w] # mass of the set of wagons (consist) (in kg) - Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) + f_Rw0 = train.f_Rw0 # coefficient for basic resistance of the set of wagons (consist) (in ‰) + f_Rw1 = train.f_Rw1 # coefficient for the consists resistance to rolling (in ‰) + f_Rw2 = train.f_Rw2 # coefficient fo the consistsr air resistance (in ‰) + m_w = train.m_w # mass of the set of wagons (consist) (in kg) - F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_w) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ -# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_w) /v00)^2 # vehicle resistance of the wagons (in N) + F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_air) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ +# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_air) /v00)^2 # vehicle resistance of the wagons (in N) return F_R_wagons end #function calcWagonsResistance diff --git a/src/import.jl b/src/import.jl deleted file mode 100644 index aae95af..0000000 --- a/src/import.jl +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -""" -Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. -""" -function importYamlFiles(trainDirectory::String, pathDirectory::String) - train = importFromYaml(:train, trainDirectory) - path = importFromYaml(:path, pathDirectory) - - return (train, path) -end #function importYamlFiles - - """ - Read the train information from a YAML file, save it in a Dict and return it. - """ -function importFromYaml(dataType::Symbol, directory::String) - dataSet = String(dataType) - data = YAML.load(open(directory)) - if collect(keys(data))[1] != dataSet - error("ERROR at reading the ", dataSet, " yaml file: The data set is called ", collect(keys(data))[1]," and not ", dataSet, ".") - end - dataKeys = collect(keys(data[dataSet])) - dataKeys = collect(keys(data[dataSet])) - dataValues = collect(values(data[dataSet])) - dictionary = Dict() - for number in 1:length(dataKeys) - merge!(dictionary, Dict(Symbol(dataKeys[number]) => dataValues[number])) - end - return dictionary -end # function importFromYaml diff --git a/src/output.jl b/src/output.jl index ae5d25b..c7165be 100644 --- a/src/output.jl +++ b/src/output.jl @@ -5,7 +5,7 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -function createOutput(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) +function createOutput(train::Train, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) if settings.outputDetail == :running_time output = movingSection[:t] # TODO: or use drivingCourse[end][:t] @@ -71,7 +71,7 @@ function createOutput(train::Dict, settings::Settings, path::Path, movingSection end #= -function createOutputDict(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) +function createOutputDict(train::Train, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict}) outputDict = Dict{Symbol,Any}() merge!(outputDict, Dict(:train => train, :path => path, :settings => settings)) diff --git a/src/types.jl b/src/types.jl index 0123f15..4a663c9 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,7 +1,7 @@ #!/usr/bin/env julia # -*- coding: UTF-8 -*- # __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg, Martin Scheidt" +# __author__ = "Martin Scheidt, Max Kannenberg" # __copyright__ = "2022" # __license__ = "ISC" @@ -28,686 +28,35 @@ struct Path end #struct Path -""" -Read the train information from a YAML file, save it in a train Dict and return it. -""" -function checkAndSetTrain!(train::Dict) - # check train information from input dictionary - - checkAndSetString!(train, "train", :name, "") # train's name - # add train's identifier if not existing - if !(haskey(train, :id) && train[:id]!=nothing) - merge!(train, Dict(:id =>1)) - end - checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight" - - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) - # TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) - - checkAndSetSpeedLimit!(train) # train's speed limit (in m/s) - checkAndSetBrakingAcceleration!(train) # a_braking - - checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg) - checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg) - checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg) - - checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg) - checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg) - if train[:m_train] <= 0.0 - error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.") - end - - checkAndSetRotationMassFactors!(train) - checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort - - # coefficients for the vehicle resistance of the traction unit - checkAndSetRealNumber!(train, "train", :Δv_t, "m/s", 15.0/3.6) # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rtd0, "‰", 0.0) # coefficient for basic resistance due to the traction units driving axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rtc0, "‰", 0.0) # coefficient for basic resistance due to the traction units carring axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :F_Rt2, "N", 0.0) # coefficient for air resistance of the traction units (in N) - - # coefficients for the vehicle resistance of the set of wagons (consist) - checkAndSetRealNumber!(train, "train", :Δv_w, "m/s", getDefault_Δv_w(train[:type])) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rw0, "‰", 0.0) # coefficient for basic resistance of the set of wagons (consist) (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw1, "‰", 0.0) # coefficient for the consists resistance to rolling (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw2, "‰", 0.0) # coefficient fo the consistsr air resistance (in ‰) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, :id, :type, - :length, :l_train, :v_limit, :v_limit_kmh, :a_braking, - :m_train, :m_t, :m_td, :m_tc, :m_w, - :ξ_train, :ξ_t, :ξ_w, :rotationMassFactor_train, :rotationMassFactor_t, :rotationMassFactor_w, - :tractiveEffortVelocityPairs, :F_T_pairs, :F_T_pairs_kmh, - :f_Rtd0, :f_Rtc0, :F_Rt2, :Δv_t, - :f_Rw0, :f_Rw1, :f_Rw2, :Δv_w] - informAboutUnusedKeys(collect(keys(train)), usedKeys::Vector{Symbol}, "train") - - return train -end #function checkAndSetTrain! - -function checkAndSetPath!(path::Path) - # check path information from input dictionary - - checkAndSetString!(path, "path", :name, "") - # TODO checkId ? path[:id] # path identifier - checkAndSetSections!(path) - checkAndSetPOIs!(path) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, - :sections, :sectionStarts, :sectionStarts_kmh, - :pointsOfInterest] - informAboutUnusedKeys(collect(keys(path)), usedKeys::Vector{Symbol}, "path") - - return path -end # function checkAndSetPath! - -function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::Bool) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) != Bool - error("ERROR at checking the input for the ",dictionaryType,": The value of the key ",String(key)," is not correct. The value has to be of type Bool.") - end - else - merge!(dictionary, Dict(key => defaultValue)) - defaultValue && println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.") - end - return dictionary -end #function checkAndSetBool! - -function checkAndSetPositiveNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0 - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetPositiveNumber! - -# first method without a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # do nothing - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -# second method with a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String, default::Real) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # set a default value - merge!(dictionary, Dict(mainKey, default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",mainKey," or its value is missing. Therefore the value ",String(mainKey),"=",default," ",unit," is used." ) - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetRealNumber! - -function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::Symbol, summand1::Symbol, summand2::Symbol) - if haskey(dictionary,sum) && dictionary[sum]!=nothing - if typeof(dictionary[sum]) <: Real && dictionary[sum] >= 0.0 - difference = abs(dictionary[sum] - (dictionary[summand1]+dictionary[summand2])) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") - end - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(sum => dictionary[summand1]+dictionary[summand2])) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." ) - end - - return dictionary -end #function checkAndSetSum! - -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String, validValues::Vector{String}) - # TODO change checkAndAddString! to checkAndAddSymbol! ? - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - for validValue in validValues - if value == validValue - return dictionary - end - end - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues) - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. It has to be one of the following String values: ", validValues,". For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! -# second method of function checkAndSetString! without validValues -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String) - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - return dictionary - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.") - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! - -function checkAndSetSpeedLimit!(train::Dict) - v_limit_temp = 0.0 - v_limit_kmh_temp = 0.0 - - if haskey(train, :v_limit) && train[:v_limit]!=nothing - if typeof(train[:v_limit]) <: Real && train[:v_limit] >= 0.0 - v_limit_temp = train[:v_limit] - else - error("ERROR at checking the input for the train: The value of v_limit is no real floating point number >=0.0.") - end - end - - if haskey(train, :v_limit_kmh) && train[:v_limit_kmh]!=nothing - if typeof(train[:v_limit_kmh]) <: Real && train[:v_limit_kmh] >= 0.0 - v_limit_kmh_temp = train[:v_limit_kmh] - else - error("ERROR at checking the input for the train: The value of v_limit_kmh is no real floating point number >=0.0.") - end - else - delete!(train, :v_limit_kmh) - end - - if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0 - difference = abs(v_limit_temp - v_limit_kmh_temp/3.6) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(train, :v_limit_kmh) - println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." ) - end - elseif v_limit_temp > 0.0 - # do nothing - elseif v_limit_kmh_temp > 0.0 - merge!(train, Dict(:v_limit => v_limit_kmh_temp/3.6)) - else - # set a default value - merge!(train, Dict(:v_limit, 1000.0/3.6)) # set speed limit to 1000 km/h - println("INFO at checking the input for the train: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",train[:v_limit]," m/s is used." ) - end - - return train -end #function checkAndSetSpeedLimit! - -function checkAndSetBrakingAcceleration!(train::Dict) - if haskey(train, :a_braking) && train[:a_braking]!=nothing - if typeof(train[:a_braking]) <: Real - if train[:a_braking] > 0.0 - train[:a_braking] =-train[:a_braking] - println("INFO at checking the input for the train: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking=",train[:a_braking]," m/s^2 is used." ) - elseif train[:a_braking] == 0.0 - error("ERROR at checking the input for the train: The value for a_braking is 0.0. The braking acceleration has to be <0.0.") - end - else - error("ERROR at checking the input for the train: The value for a_braking is no real floating point number <0.0.") - end - else - # set a default value depending on the train type - if train[:type] == "freight" - a_braking = -0.225 - elseif train[:type] == "passenger" - a_braking = -0.375 - #elseif train[:type] == "passengerSuburban" - # a_braking = -0.525 - # TODO: add suburban trains to train type? - end - - merge!(train, Dict(:a_braking => a_braking)) - println("INFO at checking the input for the train: The key for a_braking is missing. Because of the train type ",train[:type]," a_braking=",a_braking," m/s^2 will be assumed and used." ) - end - - return train -end #function checkAndSetBrakingAcceleration! - -function checkAndSetRotationMassFactors!(train::Dict) - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_train, :rotationMassFactor_train, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_t, :rotationMassFactor_t, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_w, :rotationMassFactor_w, "") - if haskey(train, :ξ_train) && train[:ξ_train]!=nothing - if train[:ξ_train]>0.0 - if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing)) - # TODO: is && train[:ξ_t]>0.0 necessary here? - difference = abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train]) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") - end - end - else - error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.") - end - else - checkAndSetPositiveNumber!(train, "train", :ξ_t, "", 1.09) - - if train[:m_w]>0.0 - default_ξ_w = 1.06 - else - default_ξ_w = 0.0 - end - checkAndSetPositiveNumber!(train, "train", :ξ_w, "", default_ξ_w) - - - ξ_train=(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit) - if ξ_train <= 0.0 - error("ERROR at checking the input for the train: The train's rotations mass factor has to be higher than 0.0 kg.") - end - merge!(train, Dict(:ξ_train => ξ_train)) - end - - return train -end #function checkAndSetRotationMassFactors! - -function checkAndSetTractiveEffortVelocityPairs!(train::Dict) # pairs of velocity and tractive effort - if haskey(train,:tractiveEffortVelocityPairs) && train[:tractiveEffortVelocityPairs]!=nothing - pairs = train[:tractiveEffortVelocityPairs] - velocityMultiplier = 1.0 - - if (haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing) && (haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing) - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs, F_T_pairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - end - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - pairs = train[:F_T_pairs] - velocityMultiplier = 1.0 - - if haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." ) - end - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - velocityMultiplier = 1000/3600 - pairs=[] - for row in 1:length(train[:F_T_pairs_kmh]) - push!(pairs, [train[:F_T_pairs_kmh][row][1]*velocityMultiplier, train[:F_T_pairs_kmh][row][2]]) - end # for - - else - error("ERROR at checking the input for the train: There has to be the key tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.") - end # if - - # check if the elements of the array have the correct type - errorDetected=false - - for row in 1:length(pairs) - if typeof(pairs[row][1]) <: Real && pairs[row][1]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - if typeof(pairs[row][2]) <: Real && pairs[row][2]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - - if row>=2 && pairs[row][1] <= pairs[row-1][1] - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," (v=",pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",pairs[row-1][1]," m/s).") - end - end # for - if errorDetected - error("ERROR at checking the input for the train: Only real floating point number >=0.0 are allowed for speed and tractive effort. The speed values have to be listed from low to high.") - end - - # create tractiveEffortVelocityPairs - if pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used - newPairs=[] - push!(newPairs, [0.0, pairs[1][2]]) - println("INFO at checking the input for the train: The tractive effort for v=0.0 m/s is missing. Therefore the first given value F_T(v=",pairs[1][1]," m/s)=",pairs[1][2]," N will be used." ) - for row in 1:length(pairs) - push!(newPairs, [pairs[row][1], pairs[row][2]]) - end # for - merge!(train, Dict(:tractiveEffortVelocityPairs => newPairs)) - else - merge!(train, Dict(:tractiveEffortVelocityPairs => pairs)) - end - - if length(pairs[1])>2 - println("INFO according the train dictionary: Only the first two columns of train[:tractiveEffortVelocityPairs] are used in this tool.") - end - - return train -end #function checkAndSetTractiveEffortVelocityPairs! - -function getDefault_Δv_w(type::String) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - if type == "passenger" - # TODO if different passenger or freight trains are posiible, use: if startswith(type, "passenger"). exanples: passengerLocomotivehauled and passengerMotorCoachTrain - Δv_w=15.0/3.6 - elseif type == "freight" - Δv_w=0.0 - end # if - - return Δv_w -end #function getDefault_Δv_w! - -# function checkAndSetSections!(path::Path) -# # check the section information -# if haskey(path,:sections) && path.sections!=nothing -# # TODO: check typeof(path.sections) == Dict -# if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) -# println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) - -# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing -# println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) - -# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing -# println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) -# end -# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing -# # TODO: check typeof(path.sections) == Array -# createSections!(path, :sectionStarts) - -# if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing -# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) -# end -# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing -# # TODO: check typeof(path.sections) == Array -# createSections!(path, :sectionStarts_kmh) -# else -# error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") -# section = Dict(:s_start => 0.0, -# :s_end => 15.0, -# :v_limit => 1000.0/3.6, -# :f_Rp => 0.0) -# merge!(path, Dict(:sections => [section])) -# return path -# end - -# sections = path.sections - -# checkedSections = [] -# increasing = false -# decreasing = false - -# #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? - -# # check values for section==1 -# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_start, "m", 0.0) # first point of the section (in m) -# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_end, "m", 0.0) # first point of the next section (in m) -# checkAndSetPositiveNumber!(sections[1], "path.sections[1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) -# checkAndSetRealNumber!(sections[1], "path.sections[1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - -# push!(checkedSections, sections[1]) - -# if sections[1][:s_start] < sections[1][:s_end] -# increasing = true -# elseif sections[1][:s_start] > sections[1][:s_end] -# decreasing = true -# else -# pop!(checkedSections) -# println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") -# end - - -# for sectionNr in 2:length(sections) -# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) -# # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values -# #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real -# # defaultEnd = sections[sectionNr+1][:s_start] -# #end -# defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! -# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) -# checkAndSetPositiveNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) -# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - -# push!(checkedSections, sections[sectionNr]) - -# # compare the section's start and end position -# if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] -# increasing = true -# elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] -# decreasing = true -# else -# pop!(checkedSections) -# println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") -# end -# if increasing && decreasing -# error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") -# end - - -# if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] -# error("ERROR at checking the input for the path.sections: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") -# # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? -# end -# end #for - -# return path -# end #function checkAndSetSections! - -# function createSections!(path::Path, key::Symbol) -# # read the section starting positions and corresponding information -# if key == :sectionStarts -# sectionStartsArray = path[:sectionStarts] -# conversionFactor = 1.0 # conversion factor between the units m/s and m/s - -# if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing -# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) -# end -# elseif key == :sectionStarts_kmh -# sectionStartsArray = path[:sectionStarts_kmh] -# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s -# elseif key == :characteristic_sections -# sectionStartsArray = path[:characteristic_sections] -# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s -# else -# error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") -# end # if - -# # check if the array is correct and if elements of the array have the correct type and valid values -# errorDetected = false -# if length(sectionStartsArray)<2 -# error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") -# end - -# for row in 1:length(sectionStartsArray) -# if length(sectionStartsArray[row])>=3 -# if length(sectionStartsArray[row])>3 -# println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") -# end -# else -# error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") -# end - -# if !(typeof(sectionStartsArray[row][1]) <: Real) -# errorDetected=true -# println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") -# end -# if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) -# errorDetected=true -# println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") -# end -# if !(typeof(sectionStartsArray[row][3]) <: Real) -# errorDetected=true -# println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") -# end -# end # for -# if errorDetected -# error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") -# end - - -# sections = [] -# for row in 2:length(sectionStartsArray) -# s_start = sectionStartsArray[row-1][1] # first point of the section (in m) -# s_end = sectionStartsArray[row][1] # first point of the next section (in m) -# v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) -# f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) - -# section = Dict(:s_start => s_start, -# :s_end => s_end, -# :v_limit => v_limit, -# :f_Rp => f_Rp) -# push!(sections, section) -# end # for -# # s_start in first entry defines the path's beginning -# # s_end in last entry defines the path's ending - -# merge!(path, Dict(:sections => sections)) -# return path -# end #function createSections! - -# function checkAndSetPOIs!(path::Path) -# # read the section starting positions and corresponding information -# if haskey(path, :pointsOfInterest) -# # if path.poi != nothing -# pointsOfInterest = path[:points_of_interest] - -# sortingNeeded = false -# errorDetected = false -# for element in 1:length(pointsOfInterest) -# if typeof(pointsOfInterest[element]) <: Real -# if element > 1 -# if pointsOfInterest[element] < pointsOfInterest[element-1] -# sortingNeeded = true -# println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") -# end -# end -# else -# errorDetected = true -# println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") -# end -# end # for - -# if errorDetected -# error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") -# end -# if sortingNeeded == true -# sort!(pointsOfInterest) -# end - -# copiedPOIs = [] -# for element in 1:length(pointsOfInterest) -# if element == 1 -# push!(copiedPOIs, pointsOfInterest[element]) -# elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] -# push!(copiedPOIs, pointsOfInterest[element]) -# end -# end # for -# path[:points_of_interest ] = copiedPOIs - -# # else -# # println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") -# # delete!(path, :points_of_interest) -# # end -# end - -# return path -# end #function checkAndSetPOIs! - -#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool -function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool - unusedKeys = [] - # find unused keys in allKeys - for key in allKeys - used = false - for usedKey in usedKeys - if key == usedKey - used = true - break - end - end - if !used - push!(unusedKeys, key) - end - end - - if length(unusedKeys)>0 - println("INFO at checking the input for the ",dictionaryType,": The following Keywords are not used in this tool:") - for key in unusedKeys - println(" - ",key) - end - end -end #function informAboutUnusedKeys +struct Train + + name::String # a name or description of the train + id::String # a short string as identifier + uuid::UUID # a unique identifier + length::Real # train length in meter + m_train_full::Real # mass of the full loaded train in kilogram + m_td::Real # mass on driving axles of the traction unit in kilogram + m_tc::Real # mass on the traction unit's carrying axles in kilogram + m_w::Real # mass of the set of wagons/cars/consist in kilogram + ξ_train::Real # rotation mass factor + ξ_loco::Real # rotation mass factor + ξ_cars::Real # rotation mass factor + transportType::Symbol # ":freight" or ":passenger" for resistance calculation + v_limit::Real # in m/s + a_braking::Real # in m/s^2 + + # coefficients for the vehicle resistance + # for the traction unit (F_Rt=f_Rtd0*m_td*g+f_Rtc0*m_tc*g+F_Rt2*((v+Δv_air)/v00)^2) + f_Rtd0::Real # coefficient for basic resistance due to the traction units driving axles (in ‰) + f_Rtc0::Real # coefficient for basic resistance due to the traction units carring axles (in ‰) + F_Rt2::Real # coefficient for air resistance of the traction units (in N) + + # for the consist (set of wagons) (F_Rw=m_w*g*(f_Rw0+f_Rw1*v/v00+f_Rw2*((v+Δv_air)/v00)^2)) + f_Rw0::Real # coefficient for the consists basic resistance (in ‰) + f_Rw1::Real # coefficient for the consists resistance to rolling (in ‰) + f_Rw2::Real # coefficient fo the consistsr air resistance (in ‰) + + # tractive effort as pairs of speed and tractive effort + tractiveEffort::Vector{Tuple{Real, Real}} # [v in m/s, F_T in N] + +end #struct Train diff --git a/test/runtests.jl b/test/runtests.jl index 3476764..655f41e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,9 +14,9 @@ settings = Dict() @testset "load data" begin println("testing load train data") - push!(trains, :freight => @time TrainRuns.importFromYaml(:train, "test/data/trains/freight.yaml")) - push!(trains, :local => @time TrainRuns.importFromYaml(:train, "test/data/trains/local.yaml")) - push!(trains, :longdistance => @time TrainRuns.importFromYaml(:train, "test/data/trains/longdistance.yaml")) + push!(trains, :freight => @time Train("test/data/trains/freight.yaml")) + push!(trains, :local => @time Train("test/data/trains/local.yaml")) + push!(trains, :longdistance => @time Train("test/data/trains/longdistance.yaml")) println("testing load path data") push!(paths, :const => @time Path("test/data/paths/const.yaml")) @@ -79,7 +79,7 @@ anticipated = Dict( @time result = trainrun(test[1][2],test[2][2]) expected = anticipated[:default][Symbol(test_name)] # compare result to test data set - @test isapprox(result, expected, atol=0.01) + @test isapprox(result, expected, rtol=0.1) println("--------------------") end From 840d1d0e46da936a52bc918e9bf2a3cc784d8938 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 12 May 2022 16:50:27 +0200 Subject: [PATCH 15/18] removed CSV export --- CHANGELOG.md | 4 +- Project.toml | 2 - README.md | 1 - docs/DEFAULT.yaml | 3 +- src/TrainRuns.jl | 5 +- src/constructors.jl | 12 +- src/export.jl | 212 ----------------------------- src/types.jl | 3 +- test/data/settings/csv_export.yaml | 5 - test/runtests.jl | 1 - 10 files changed, 10 insertions(+), 238 deletions(-) delete mode 100644 src/export.jl delete mode 100644 test/data/settings/csv_export.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index f0281f1..9ccca20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,11 +32,13 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * updated test files to railtoolkit/schema (2022.05) ### Removed -* dependency Plots +* dependency Plots and CSV * AdditionalOutput.jl * EnergySaving.jl * test/testEnums.jl * import.jl +* export.jl +* settings for CSV export ## Version [0.8] 2022-01-20 diff --git a/Project.toml b/Project.toml index 1fbfcb9..00f601c 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,6 @@ authors = ["Max Kannenberg", "Martin Scheidt", "contributors"] version = "0.8.0" [deps] -CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" @@ -13,7 +12,6 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" [compat] -CSV = "^0" DataFrames = "^1" JSONSchema = "^1" YAML = "^0" diff --git a/README.md b/README.md index 56a7171..68dd68f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ The required julia packages are - YAML.jl - JSONSchema.jl - DataFrames.jl - - CSV.jl ------------ diff --git a/docs/DEFAULT.yaml b/docs/DEFAULT.yaml index 1a81cff..1a562d9 100644 --- a/docs/DEFAULT.yaml +++ b/docs/DEFAULT.yaml @@ -7,5 +7,4 @@ settings: 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" + outputFormat: "dataframe" # output as "dataframe" or as "dict" diff --git a/src/TrainRuns.jl b/src/TrainRuns.jl index 54997db..d6de6ad 100644 --- a/src/TrainRuns.jl +++ b/src/TrainRuns.jl @@ -11,11 +11,11 @@ module TrainRuns ## loading standard library packages using UUIDs, Dates, Statistics ## loading external packages -using YAML, JSONSchema, CSV, DataFrames +using YAML, JSONSchema, DataFrames export ## Interface -trainrun, Train, Path, Settings, exportToCsv +trainrun, Train, Path, Settings ## global variables global g = 9.80665 # acceleration due to gravity (in m/s^2) @@ -30,7 +30,6 @@ include("calc.jl") include("characteristics.jl") include("behavior.jl") include("output.jl") -include("export.jl") ## main function """ diff --git a/src/constructors.jl b/src/constructors.jl index d6d156f..cf83304 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -26,8 +26,7 @@ function Settings(file="DEFAULT") stepSize = 20 approxLevel = 3 outputDetail = :running_time - outputFormat = :julia_dict - outputDir = "." + outputFormat = :dataframe ## load from file if file != "DEFAULT" @@ -58,11 +57,7 @@ function Settings(file="DEFAULT") "outputFormat": { "description": "Output format", "type": "string", - "enum": [ "julia_dict", "csv" ] - }, - "outputDir": { - "description": "Path for the CSV export", - "type": "string" + "enum": [ "dataframe", "dict" ] } } }""") @@ -84,10 +79,9 @@ function Settings(file="DEFAULT") 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 - Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) + Settings(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat) end #function Settings() # outer constructor diff --git a/src/export.jl b/src/export.jl deleted file mode 100644 index 7113866..0000000 --- a/src/export.jl +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -function exportToCsv(runningTime::AbstractFloat, settings::Settings) - createCsvFile(runningTime, settings) - - return true -end - -function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Settings) - createCsvFile(dataPointsToExport, settings) - - return true -end - -function exportToCsv(output::Dict) - 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][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumRunningTime] - else - dataPointsToExport = output[:drivingCourseMinimumRunningTime] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - if output[:settings][:operationModeMinimumEnergyConsumption] == true - operationMode = "minimum energy consumption" - if output[:settings][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumEnergyConsumption] - else - dataPointsToExport = output[:drivingCourseMinimumEnergyConsumption] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - return true - end - return false -end #function exportToCsv - -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 outputDir - date = Dates.now() - dateString = Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - - csvFilePath = settings[:outputDir]*"/"*dateString*"_RunningTime.csv" - - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -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] - - allColumns = Array{Any,1}[] - for column in 1:length(header) - currentColumn = Any[] - push!(currentColumn, header[column]) - for point in dataPointsToExport - push!(currentColumn, point[columnSymbols[column]]) - end - push!(allColumns, currentColumn) - end # for - - - # 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 - println("") - end - - date = Dates.now() - dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - csvFilePath=settings[:outputDir]*"/"*dateString*"_DataPoints.csv" - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -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) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - 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]] - push!(accumulatedData, row) # push row to accumulatedData - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile - - - -#= -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) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - 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 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]] - push!(accumulatedData, row) # push row to accumulatedData - end - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # 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 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[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile -=# diff --git a/src/types.jl b/src/types.jl index 4a663c9..0441086 100644 --- a/src/types.jl +++ b/src/types.jl @@ -13,8 +13,7 @@ struct Settings 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". + outputFormat::Symbol # output as ":dataframe" or as ":dict". end #struct Settings diff --git a/test/data/settings/csv_export.yaml b/test/data/settings/csv_export.yaml deleted file mode 100644 index 5cd9517..0000000 --- a/test/data/settings/csv_export.yaml +++ /dev/null @@ -1,5 +0,0 @@ -%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/runtests.jl b/test/runtests.jl index 655f41e..af73926 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,7 +33,6 @@ settings = Dict() push!(settings, "time" => @time Settings("test/data/settings/time.yaml")) push!(settings, "timestrip" => @time Settings("test/data/settings/time_strip.yaml")) push!(settings, "velocity" => @time Settings("test/data/settings/velocity.yaml")) - push!(settings, "csvexport" => @time Settings("test/data/settings/csv_export.yaml")) @test typeof(first(paths)[2]) == Path @test typeof(first(settings)[2]) == Settings From b2fc3ff73e457c32dbb315acf0cf3622510e81ea Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 12 May 2022 17:37:00 +0200 Subject: [PATCH 16/18] debug GitHub actions --- .github/workflows/CI.yml | 4 ++-- src/constructors.jl | 2 +- test/runtests.jl | 28 ++++++++++++++-------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f104c87..3b1d808 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -26,9 +26,9 @@ jobs: - macOS-latest - windows-latest arch: - - x86 + # - x86 - x64 - - aarch64 + # - aarch64 steps: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 diff --git a/src/constructors.jl b/src/constructors.jl index cf83304..b02562e 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -15,7 +15,7 @@ The function Settings() will create a set of settings for the train run calculat # Example ```jldoctest julia> my_settings = Settings() # will generate default settings -Settings(mass_point, :distance, 20, 3, running_time, julia_dict, ".") +Settings(mass_point, :distance, 20, 3, running_time, :dataframe) ``` """ function Settings(file="DEFAULT") diff --git a/test/runtests.jl b/test/runtests.jl index af73926..f97be1f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,25 +14,25 @@ settings = Dict() @testset "load data" begin println("testing load train data") - push!(trains, :freight => @time Train("test/data/trains/freight.yaml")) - push!(trains, :local => @time Train("test/data/trains/local.yaml")) - push!(trains, :longdistance => @time Train("test/data/trains/longdistance.yaml")) + push!(trains, :freight => @time Train("data/trains/freight.yaml")) + push!(trains, :local => @time Train("data/trains/local.yaml")) + push!(trains, :longdistance => @time Train("data/trains/longdistance.yaml")) println("testing load path data") - push!(paths, :const => @time Path("test/data/paths/const.yaml")) - push!(paths, :slope => @time Path("test/data/paths/slope.yaml")) - push!(paths, :speed => @time Path("test/data/paths/speed.yaml")) - push!(paths, :realworld => @time Path("test/data/paths/realworld.yaml")) + push!(paths, :const => @time Path("data/paths/const.yaml")) + push!(paths, :slope => @time Path("data/paths/slope.yaml")) + push!(paths, :speed => @time Path("data/paths/speed.yaml")) + push!(paths, :realworld => @time Path("data/paths/realworld.yaml")) println("testing load settings data") push!(settings, "default" => @time Settings()) - push!(settings, "poi" => @time Settings("test/data/settings/points_of_interest.yaml")) - push!(settings, "drivingcourse" => @time Settings("test/data/settings/driving_course.yaml")) - push!(settings, "everything" => @time Settings("test/data/settings/everything.yaml")) - push!(settings, "strip" => @time Settings("test/data/settings/strip.yaml")) - push!(settings, "time" => @time Settings("test/data/settings/time.yaml")) - push!(settings, "timestrip" => @time Settings("test/data/settings/time_strip.yaml")) - push!(settings, "velocity" => @time Settings("test/data/settings/velocity.yaml")) + push!(settings, "poi" => @time Settings("data/settings/points_of_interest.yaml")) + push!(settings, "drivingcourse" => @time Settings("data/settings/driving_course.yaml")) + push!(settings, "everything" => @time Settings("data/settings/everything.yaml")) + push!(settings, "strip" => @time Settings("data/settings/strip.yaml")) + push!(settings, "time" => @time Settings("data/settings/time.yaml")) + push!(settings, "timestrip" => @time Settings("data/settings/time_strip.yaml")) + push!(settings, "velocity" => @time Settings("data/settings/velocity.yaml")) @test typeof(first(paths)[2]) == Path @test typeof(first(settings)[2]) == Settings From 7793552eeafe1b6198c12d48383c4de2f0c2f09c Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Thu, 12 May 2022 22:40:13 +0200 Subject: [PATCH 17/18] cars of train are now assumed as fully loaded --- src/constructors.jl | 13 +++++++++---- test/runtests.jl | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/constructors.jl b/src/constructors.jl index b02562e..ffa27c1 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -295,7 +295,8 @@ function Train(file, type = :YAML) m_loco = 0 # in kilogram m_td = 0 # in kilogram m_tc = 0 # in kilogram - m_w = 0 # in kilogram + m_car_full = 0 # in kilogram + m_car_empty = 0 # in kilogram ξ_train = 1.08 # rotation mass factor, source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 13 for "Zug, überschlägliche Berechnung" ξ_loco = 1.09 # rotation mass factor ξ_cars = 1.06 # rotation mass factor @@ -580,7 +581,11 @@ function Train(file, type = :YAML) haskey(car.data, "rotation_mass") ? append!(rotMassFac,repeat([(car.data["rotation_mass"],car.data["mass"])],car.n)) : append!(rotMassFac,repeat([(ξ_cars ,car.data["mass"])],car.n)) - m_w += car.data["mass"] * car.n * 1000 # in kg + m_car_empty += car.data["mass"] * car.n * 1000 # in kg + m_car_full += car.data["mass"] * car.n * 1000 # in kg + haskey(car.data, "load_limit") ? + m_car_full += car.data["load_limit"] * car.n * 1000 : # in kg + nothing end f_Rw0 = Statistics.mean(resis_base) f_Rw1 = Statistics.mean(resis_roll) @@ -589,7 +594,7 @@ function Train(file, type = :YAML) for elem in rotMassFac carRotMass += elem[1]*elem[2] * 1000 # in kg end - ξ_cars = carRotMass/m_w + ξ_cars = carRotMass/m_car_empty ξ_train = (ξ_loco * m_loco+ carRotMass)/m_train_empty else ξ_cars = 0 @@ -598,7 +603,7 @@ function Train(file, type = :YAML) Train( name, id, uuid, length, - m_train_full, m_td, m_tc, m_w, + m_train_full, m_td, m_tc, m_car_full, ξ_train, ξ_loco, ξ_cars, transportType, v_limit, a_braking, diff --git a/test/runtests.jl b/test/runtests.jl index f97be1f..167e358 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -78,7 +78,7 @@ anticipated = Dict( @time result = trainrun(test[1][2],test[2][2]) expected = anticipated[:default][Symbol(test_name)] # compare result to test data set - @test isapprox(result, expected, rtol=0.1) + @test isapprox(result, expected, rtol=0.01) println("--------------------") end From d2b895c8807c37a2f433c1921858ca46df9d8688 Mon Sep 17 00:00:00 2001 From: Max Kannenberg <95709892+MaxKannenberg@users.noreply.github.com> Date: Tue, 17 May 2022 11:16:45 +0200 Subject: [PATCH 18/18] Update LICENSE Correct a typing error --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 58a4eaf..b5a52ad 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License (ISC) -Copyright 2022 Max Kannenberg, Martin scheidt +Copyright 2022 Max Kannenberg, Martin Scheidt Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice