new type Settings as struct

development
Martin Scheidt 2022-04-28 17:02:40 +02:00
parent 25959f7302
commit e2842157da
42 changed files with 485 additions and 1836 deletions

View File

@ -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

View File

@ -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]

View File

@ -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"]

View File

@ -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)
[![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)
------------

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

11
docs/DEFAULT.yaml Normal file
View File

@ -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"

View File

@ -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

View File

@ -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.")

View File

@ -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("")

View File

@ -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.")

View File

@ -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

View File

@ -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,38 +371,38 @@ 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]
@ -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<v_peak and s<s_exit in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s")
error("ERROR at accelerating section: With the step variable ",settings.stepVariable," the while loop will be left although v<v_peak and s<s_exit 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)
@ -551,15 +536,15 @@ 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::Dict, train::Dict, CSs::Vector{Dict}, cruisingType::String)
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])
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
if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics]
@ -587,14 +572,14 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
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])
#03/25 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
if settings[:massModel]=="homogeneous strip" && CS[:id] > 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_braking<s_exit && v>0.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_braking<s_exit && v>0.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<v_peak and s+s_braking<s_exit in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s")
error("ERROR at coasting until braking section: With the step variable ",settings.stepVariable," the while loop will be left although v<v_peak and s+s_braking<s_exit 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)
@ -1158,7 +1143,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::Dict, train::Dict, CSs::Vector{Dict})
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]
@ -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

View File

@ -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.")
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
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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
function checkAndSetInput!(train::Dict, path::Dict, settings::Settings)
checkAndSetTrain!(train)
checkAndSetPath!(path)
checkAndSetSettings!(settings)
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'.")
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, settings)
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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,4 @@
%YAML 1.2
---
settings:
massModel: "homogeneous_strip" # type of train model used: "mass_point" or "homogeneous_strip"

View File

@ -0,0 +1,4 @@
%YAML 1.2
---
settings:
stepVariable: "time" # variable of the linear multistep method: "distance", "time" or "velocity"

View File

@ -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"

View File

@ -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

View File

@ -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
# TODO:
@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
end
end
println("test finished")
# TODO:
# print test results
end

View File

@ -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