new type Path as a struct, working test sets, structure rework

development
Martin Scheidt 2022-05-04 16:34:17 +02:00
parent 9b00bb8b12
commit 00eda496f5
21 changed files with 1173 additions and 889 deletions

View File

@ -18,12 +18,17 @@ jobs:
fail-fast: false
matrix:
version:
- '1.6'
- '1.7'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
arch:
- x86
- x64
- aarch64
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1

View File

@ -15,12 +15,19 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security.
### Changed
* replaced settings::Dict with type Settings as struct
* replaced path::Dict with type Path as struct
* restructured examples/ and data/ in docs/ and test/
* modified test to work with Julia Testsets and with simplier naming of input files
* renamed Validate.jl into types.jl
* renamed TrainRunCalc.jl into calc.jl
* changed tilte of include files from upper case to lower case
* moved trainrun function from calc.jl to TrainRun.jl
* moved createDataPoint() from behavior.jl to types.jl
* moved createBehaviorSection() from behavior.jl to types.jl
* moved createMovingSection() from characteristics.jl to types.jl
* moved createCharacteristicSection() from characteristics.jl to types.jl
* changed title of include files from upper case to lower case
* changed seperation of submodules into a single module with file include
* updated test files to railtoolkit/schema (2022.04)
### Removed
* dependency Plots

View File

@ -5,28 +5,77 @@ email, or any other method with the owners of this repository before making a ch
Please note we have a code of conduct, please follow it in all your interactions with the project.
# Julia Development Environment
## Julia Development Environment
Link your local git repository to Julia:
```console
$ ln -s ~/path/to/TrainRun.jl ~/.julia/dev/TrainRun
```
Have a look here how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev
Have a look how to develop Julia packages: https://github.com/ShozenD/julia-pkg-dev
You might want to use `Revise.jl` as well:
```julia
Pkg.add("Revise")
```
and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file)
and then just load with `using Revise` (preferably by putting it in the `~/.julia/config/startup.jl` file).
You can overide the standard TrainRun package with the local development branch (see linking above) with:
```julia
julia> # use the ] key
(@v1.x) pkg> develop TrainRun
(@v1.x) pkg> # use backspace
julia> using TrainRun # local development branch will be loaded
```
If you want to add a dependency use:
```julia
julia> # use the ] key
(@v1.x) pkg> activate TrainRun
(TrainRun) pkg>
```
## TrainRun files
# Pull Request Process
TODO!
* add your changes to the CHANGELOG.md under [Unreleased]
* TODO!
## Reporting Issues
* It's always good to start with a quick search for an existing issue to post on,
or related issues for context, before opening a new issue
* Including minimal examples is greatly appreciated
* If it's a bug, or unexpected behaviour, reproducing on the latest development version
(`Pkg.add(name="TrainRun", rev="master")`) is a good gut check and can streamline the process,
along with including the first two lines of output from `versioninfo()`
## Style Guidelines
TODO
## Git Recommendations For Pull Requests
* Avoid working from the `master` branch of your fork, creating a new branch will make it
easier if TrainRun.jl `master` branch changes and you need to update your pull request;
* All PRs and issues should be opened against the `master` branch not against the current release;
* Run tests of your code before sending any commit to GitHub. Only push changes when
the tests of the change are passing locally. In particular note that it is not a problem
if you send several commits in one push command to GitHub as CI will be run only once then;
* If any conflicts arise due to changes in TrainRun.jl `master` branch, prefer updating your pull
request branch with `git rebase` (rather than `git merge`), since the latter will introduce a merge
commit that might confuse GitHub when displaying the diff of your PR, which makes your changes more
difficult to review. Alternatively use conflict resolution tool available at GitHub;
* Please try to use descriptive commit messages to simplify the review process;
* Using `git add -p` or `git add -i` can be useful to avoid accidently committing unrelated changes;
* Maintainers get notified of all changes made on GitHub. However, what is useful is writing a short
message after a sequence of changes is made summarizing what has changed and that the PR is ready
for a review;
* When linking to specific lines of code in discussion of an issue or pull request, hit the `y` key
while viewing code on GitHub to reload the page with a URL that includes the specific commit that
you're viewing. That way any lines of code that you refer to will still be correct in the future, even
if additional commits are pushed to the branch you are reviewing;
* Please make sure you follow the code formatting guidelines when submitting a PR;
Also preferably do not modify parts of code that you are not editing as this makes
reviewing the PR harder (it is better to open a separate maintenance PR
if e.g. code layout can be improved);
* If a PR is not finished yet and should not be reviewed yet then it should be opened as DRAFT
(in this way maintainers will know that they can ignore such PR until it is made non-draft or the author
asks for a review).

View File

@ -1,6 +1,6 @@
name = "TrainRun"
uuid = "e4541106-d44c-4e00-b50b-ecdf479fcf92"
authors = ["Max Kannenberg, Martin Scheidt, and contributors"]
authors = ["Max Kannenberg", "Martin Scheidt", "contributors"]
version = "0.8.0"
[deps]
@ -8,10 +8,15 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
[compat]
julia = "1.7"
CSV = "^0"
DataFrames = "^1"
JSONSchema = "^1"
YAML = "^0"
julia = "^1.6"
[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

View File

@ -15,27 +15,24 @@ TrainRun.jl is a step towards open science and open data in railway engineering.
The required julia packages are
- YAML.jl
- JSONSchema.jl
- Dates.jl
- DataFrames.jl
- CSV.jl
- Plots.jl
Review the settings.yaml file for your appropriate settings.
------------
# Minimal working example
```julia
include("../src/TrainRun.jl")
using .TrainRun
import TrainRun
train_directory = "data/trains/train_freight_V90withOreConsist.yaml"
running_path_directory = "data/paths/path_1_10km_nConst_vConst.yaml"
settings_directory = "data/settings.yaml"
(train, running_path, settings) = importYamlFiles(train_directory, running_path_directory, setting_directory)
train = Train("test/data/trains/freight.yaml")
path = Path("test/data/paths/const.yaml")
train_run = trainRun(train, running_path, settings)
runtime = trainrun(train, path)
println("The train needs $runtime seconds for the running path.")
```
------------

View File

@ -22,7 +22,7 @@ for path in paths
for train in trains
# println("train: ", train[:name])
for settings in settings
resultsDict = trainRun(train, path, settings)
resultsDict = trainrun(train, path, settings)
if haskey(settings, :outputFormat) && settings[:outputFormat] == "CSV"
exportToCsv(resultsDict, settings)
sleep(2)

View File

@ -1,10 +1,10 @@
#!/usr/bin/env julia
import TrainRun
using TrainRun
train = Train("data/trains/train_freight_V90withOreConsist.yaml")
path = Path("data/paths/path_1_10km_nConst_vConst.yaml")
train = Train("test/data/trains/freight.yaml")
path = Path("test/data/paths/const.yaml")
runtime = trainRun(train, path)
runtime = trainrun(train, path)
println("The train needs $runtime seconds for the running path.")

View File

@ -8,8 +8,14 @@ __precompile__(true)
module TrainRun
## loading standard library packages
using UUIDs, Dates
## loading external packages
using YAML, JSONSchema, CSV, DataFrames, Dates
using YAML, JSONSchema, CSV, DataFrames
export
## Interface
trainrun, Path, Settings, exportToCsv
## include package files
include("types.jl")
@ -21,10 +27,42 @@ include("import.jl")
include("export.jl")
include("calc.jl")
export
## Interface
trainRun, Settings, exportToCsv
## main function
"""
trainrun(train::Dict, path::Path, settings::Settings)
## main functions
Calculate the running time of a `train` on a `path`.
The `settings` provides the choice of models for the calculation.
`settings` can be omitted. If so, a default is used.
The running time will be return in seconds.
# Examples
```julia-repl
julia> trainrun(train, path)
xxx.xx # in seconds
```
"""
function trainrun(trainInput::Dict, path::Path, settings=Settings()::Settings)
# copy Input data for not changing them
# TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && isempty(path.poi)
train = copy(trainInput)
# check the input data
train = checkAndSetTrain!(train)
settings.outputDetail == :everything && println("The input has been checked.")
# prepare the input data
movingSection = determineCharacteristics(path, train, settings)
settings.outputDetail == :everything && println("The moving section has been prepared.")
# calculate the train run for oparation mode "minimum running time"
(movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train)
settings.outputDetail == :everything && println("The driving course for the shortest running time has been calculated.")
# accumulate data and create an output dictionary
output = createOutput(train, settings, path, movingSection, drivingCourse)
return output
end # function trainrun
end # module TrainRun

View File

@ -176,9 +176,12 @@ function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::
return currentSpeedLimit
end #function getCurrentSpeedLimit
function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real)
"""
?
"""
function getNextPointOfInterest(pointsOfInterest::Vector{Tuple}, s::Real)
for s_POI in pointsOfInterest
if s_POI > s
if s_POI[1] > s
return s_POI
end
end
@ -327,7 +330,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
while !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached && !previousSpeedLimitReached
currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
if !ignoreBraking
@ -335,7 +338,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
end
while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached
# 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit
# 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit
if drivingCourse[end][:s] >= currentSpeedLimit[:s_end]
# could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long
currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length])
@ -360,7 +363,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
previousSpeedLimitReached = currentSpeedLimit[:v] < CS[:v_limit] && (drivingCourse[end][:v] > currentSpeedLimit[:v] || (drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end]))
targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak]
#targetSpeedReached = speedLimitReached
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well
tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R]
end #while
@ -380,10 +383,10 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing
currentStepSize = settings.stepSize / 10.0^cycle
elseif drivingCourse[end][:s] > nextPointOfInterest
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest[1]) # for testing
if settings.stepVariable == :distance
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
@ -420,16 +423,16 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing
break
elseif drivingCourse[end][:s] == nextPointOfInterest
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing
if nextPointOfInterest == CS[:s_exit]
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest[1]) # for testing
if nextPointOfInterest[1] == CS[:s_exit]
endOfCSReached = true
end
break
else
println("v=",drivingCourse[end][:v]," v_peak= ", CS[:v_peak] , " v_cLimit=", currentSpeedLimit[:v])
println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest)
println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest[1])
println("F_T=",drivingCourse[end][:F_T] ," F_R=", drivingCourse[end][:F_R])
error("ERROR at accelerating section: With the step variable ",settings.stepVariable," the while loop will be left although v<v_peak and s<s_exit in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s")
@ -467,9 +470,9 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
end
elseif drivingCourse[end][:s] > nextPointOfInterest
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing
drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
elseif drivingCourse[end][:F_T] <= drivingCourse[end][:F_R]
@ -590,12 +593,12 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
# use the conditions for the cruising section
while trainInPreviousCS && !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used
currentStepSize = settings.stepSize
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
nextPointOfInterest[1] = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used
# 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R]
# 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R]
# the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS
#TODO: maybe just consider former CS with different path resistance?
# tractive effort (in N):
@ -629,7 +632,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
#end
# conditions for the next while cycle
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1] # POIs include s_exit as well
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising
trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length]
@ -647,9 +650,9 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
elseif trainIsBrakingDownhill && !resistingForceNegative
currentStepSize = settings.stepSize / 10.0^cycle
elseif drivingCourse[end][:s] > nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
if settings.stepVariable == :distance
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
@ -667,7 +670,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length]
break
elseif drivingCourse[end][:s] == nextPointOfInterest
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
break
elseif !trainInPreviousCS
@ -689,8 +692,8 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
resistingForceNegative = drivingCourse[end][:F_R] < 0.0
else # if the level of approximation is reached
if drivingCourse[end][:s] > nextPointOfInterest
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
if drivingCourse[end][:s] > nextPointOfInterest[1]
drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
elseif drivingCourse[end][:s] > BS[:s_entry]+s_cruising
if BS[:type] != "clearing"
@ -723,7 +726,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
while !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used
# 03/09 old: while drivingCourse[end][:s] < BS[:s_entry]+s_cruising && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R]
nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]))
nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])[1])
# tractive effort (in N):
#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R]))
@ -739,7 +742,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
# calculate the remaining cruising way
#s_cruisingRemaining=BS[:s_entry] + s_cruising-drivingCourse[end][:s]
s_cruisingRemaining = min(nextPointOfInterest -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s])
s_cruisingRemaining = min(nextPointOfInterest[1] -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s])
# create the next data point
push!(drivingCourse, moveAStep(drivingCourse[end], :distance, s_cruisingRemaining, CS[:id]))
@ -818,12 +821,12 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached
currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])[1]
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached
# 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end
# 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1] && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end
# acceleration (in m/s^2):
drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train])
@ -839,7 +842,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
end
brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit]
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
targetSpeedReached = drivingCourse[end][:v] <= 0.0
tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R]
endOfCSReached = drivingCourse[end][:s] == CS[:s_exit]
@ -867,10 +870,10 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing
currentStepSize = settings.stepSize / 10.0^cycle
elseif drivingCourse[end][:s] > nextPointOfInterest
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest[1]) # for testing
if settings.stepVariable == :distance
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
@ -879,8 +882,8 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing
break
elseif drivingCourse[end][:s] == nextPointOfInterest
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest[1]) # for testing
break
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R]
@ -924,9 +927,9 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
tractionDeficit = true
endOfCSReached = false
elseif drivingCourse[end][:s] > nextPointOfInterest
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing
drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
elseif drivingCourse[end][:F_T] >= drivingCourse[end][:F_R]
@ -936,7 +939,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag
else
testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: else with v=", drivingCourse[end][:v]," > 0.0 and F_T=", drivingCourse[end][:F_T]," <= F_R=", drivingCourse[end][:F_R]) # for testing
#println(" and s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," <= s_exit=",CS[:s_exit]) # for testing
#println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest=",nextPointOfInterest) # for testing
#println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing
# if drivingCourse[end][:s] + s_braking == CS[:s_exit]
# brakingStartReached = true
@ -998,12 +1001,12 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
while !targetSpeedReached && !endOfCSReached && !brakingStartReached
currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
nextPointOfInterest[1] = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while !targetSpeedReached && !brakingStartReached && !pointOfInterestReached
# 03/09 old : while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:v] <= CS[:v_peak] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest
# 03/09 old : while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:v] <= CS[:v_peak] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest[1]
# traction effort and resisting forces (in N):
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel)
@ -1018,7 +1021,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
# conditions for the next while cycle
s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking])
brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit]
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak]
end # while
@ -1030,11 +1033,11 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing
currentStepSize = settings.stepSize / 10.0^cycle
elseif drivingCourse[end][:s] > nextPointOfInterest
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing
if settings.stepVariable == :distance
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
@ -1061,8 +1064,8 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," == v_exit=", CS[:v_exit]) # for testing
break
elseif drivingCourse[end][:s] == nextPointOfInterest
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest[1]=",nextPointOfInterest[1]) # for testing
break
else
@ -1105,8 +1108,8 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::
pointOfInterestReached = false
# targetSpeedReached = true
elseif drivingCourse[end][:s] > nextPointOfInterest
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
else
# do nothing for example for drivingCourse[end][:s] + s_braking == CS[:s_exit]
@ -1156,11 +1159,11 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
while !targetSpeedReached && !endOfCSReached
currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections
nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation
while !targetSpeedReached && !endOfCSReached && !pointOfInterestReached
# 03/09 old: while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest
# 03/09 old: while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest[1]
# traction effort and resisting forces (in N):
calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel)
@ -1184,7 +1187,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
#println(drivingCourse[end][:i],". s=",drivingCourse[end][:s]," s_exit=", CS[:s_exit]," v_exit=", CS[:v_exit]," v=",drivingCourse[end][:v])
# conditions for the next while cycle
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest
pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest[1]
endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit]
targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit]
end # while
@ -1193,14 +1196,14 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
# TODO: is there a better way than rounding like in the following?
if cycle < settings.approxLevel+1
if drivingCourse[end][:v] < CS[:v_exit]
if settings.stepVariable == velocity
if settings.stepVariable == :velocity
currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
elseif drivingCourse[end][:s] > nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
if settings.stepVariable == :distance
currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s]
currentStepSize = nextPointOfInterest[1] - drivingCourse[end-1][:s]
else
currentStepSize = settings.stepSize / 10.0^cycle
end
@ -1217,7 +1220,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
targetSpeedReached = true
# println(" with a=", drivingCourse[end-1][:a]) # for testing
break
elseif drivingCourse[end][:s] == nextPointOfInterest
elseif drivingCourse[end][:s] == nextPointOfInterest[1]
break
end
@ -1243,8 +1246,8 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
# recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit])
drivingCourse[end][:s] = CS[:s_exit]
break
elseif drivingCourse[end][:s] > nextPointOfInterest
drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest
elseif drivingCourse[end][:s] > nextPointOfInterest[1]
drivingCourse[end][:s] = nextPointOfInterest[1] # round s down to nextPointOfInterest
drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s]
break
elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit]
@ -1266,7 +1269,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D
targetSpeedReached = true
break
else
# do nothing for example for drivingCourse[end][:s]==nextPointOfInterest
# do nothing for example for drivingCourse[end][:s]==nextPointOfInterest[1]
end
end
end #for
@ -1332,47 +1335,6 @@ function mergeBehaviorSection!(BSs::Dict, BS::Dict)
return BSs
end #function mergeBehaviorSection!
function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer)
BS= Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill
:type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill"
:length => 0.0, # total length (in m)
:s_entry => s_entry, # first position (in m)
:s_exit => 0.0, # last position (in m)
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:v_entry => v_entry, # entry speed (in m/s)
:v_exit => 0.0, # exit speed (in m/s)
:dataPoints => [startingPoint]) # list of identifiers of the containing data points starting with the initial point
return BS
end #function createBehaviorSection
"""
a data point is the smallest element of the driving course. One step of the step approach is between two data points
"""
function createDataPoint()
dataPoint = Dict(:i => 0, # identifier and counter variable of the dricing course
:behavior => "", # type of behavior section the data point is part of ("breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill")
# a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter
:s => 0.0, # position (in m)
:Δs => 0.0, # step size (in m)
:t => 0.0, # point in time (in s)
:Δt => 0.0, # step size (in s)
:v => 0.0, # velocity (in m/s)
:Δv => 0.0, # step size (in m/s)
:a => 0.0, # acceleration (in m/s^2)
:W => 0.0, # mechanical work (in Ws)
:ΔW => 0.0, # mechanical work in this step (in Ws)
:E => 0.0, # energy consumption (in Ws)
:ΔE => 0.0, # energy consumption in this step (in Ws)
:F_T => 0.0, # tractive effort (in N)
:F_R => 0.0, # resisting force (in N)
:R_path => 0.0, # path resistance (in N)
:R_train => 0.0, # train resistance (in N)
:R_traction => 0.0, # traction unit resistance (in N)
:R_wagons => 0.0) # set of wagons resistance (in N)
return dataPoint
end #function createDataPoint
function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target)
currentPoint = drivingCourse[end]
previousPoint = drivingCourse[end-1]

View File

@ -5,42 +5,7 @@
# __copyright__ = "2020-2022"
# __license__ = "ISC"
# Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`.
"""
trainRun(train::Dict, path::Dict, settings::Settings)
Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding dictionaries `train`, `path`, `settings`.
# Examples
```julia-repl
julia> trainRun(trainDict, pathDict)
todo !!!
```
"""
function trainRun(trainInput::Dict, pathInput::Dict, settings=Settings()::Settings)
# copy Input data for not changing them
# TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest)
train = copy(trainInput)
path = copy(pathInput)
# check the input data
(train, path) = checkAndSetInput!(train, path, settings)
settings.outputDetail == :everything && println("The input has been checked.")
# prepare the input data
movingSection = determineCharacteristics(path, train, settings)
settings.outputDetail == :everything && println("The moving section has been prepared.")
# calculate the train run for oparation mode "minimum running time"
(movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train)
settings.outputDetail == :everything && println("The driving course for the shortest running time has been calculated.")
# accumulate data and create an output dictionary
output = createOutput(train, settings, path, movingSection, drivingCourse)
return output
end # function trainRun
# Calculate the running time of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`.
# calculate a train run focussing on using the minimum possible running time
function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict)

View File

@ -6,8 +6,8 @@
# __license__ = "ISC"
## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior
function determineCharacteristics(path::Dict, train::Dict, settings::Settings)
movingSection = createMovingSection(path, train[:v_limit])
function determineCharacteristics(path::Path, train::Dict, settings::Settings)
movingSection = createMovingSection(path, train[:v_limit], train[:length])
movingSection = secureBrakingBehavior!(movingSection, train[:a_braking])
movingSection = secureAcceleratingBehavior!(movingSection, settings, train)
#movingSection = secureCruisingBehavior!(movingSection, settings, train)
@ -15,77 +15,6 @@ function determineCharacteristics(path::Dict, train::Dict, settings::Settings)
return movingSection
end #function determineCharacteristics
## create a moving section containing characteristic sections
function createMovingSection(path::Dict, v_trainLimit::Real)
# this function creates and returns a moving section dependent on the paths attributes
s_entry = path[:sections][1][:s_start] # first position (in m)
s_exit = path[:sections][end][:s_end] # last position (in m)
pathLength = s_exit - s_entry # total length (in m)
CSs=Vector{Dict}()
s_csStart=s_entry
csId=1
for row in 2:length(path[:sections])
previousSection = path[:sections][row-1]
currentSection = path[:sections][row]
speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit)
pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp]
if speedLimitIsDifferent || pathResistanceIsDifferent
# 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp]
push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), path))
s_csStart = currentSection[:s_start]
csId = csId+1
end #if
end #for
push!(CSs, createCharacteristicSection(csId, s_csStart, path[:sections][end], min(path[:sections][end][:v_limit], v_trainLimit), path))
movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore
:length => pathLength, # total length (in m)
:s_entry => s_entry, # first position (in m)
:s_exit => s_exit, # last position (in m)
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:characteristicSections => CSs) # list of containing characteristic sections
return movingSection
end #function createMovingSection
## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections.
function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, path::Dict)
# Create and return a characteristic section dependent on the paths attributes
characteristicSection= Dict(:id => id, # identifier
:s_entry => s_entry, # first position (in m)
:s_exit => section[:s_end], # last position (in m)
:length => section[:s_end] -s_entry, # total length (in m)
:r_path => section[:f_Rp], # path resistance (in ‰)
:behaviorSections => Dict(), # list of containing behavior sections
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:v_limit => v_limit, # speed limit (in m/s)
# initializing :v_entry, :v_peak and :v_exit with :v_limit
:v_peak => v_limit, # maximum reachable speed (in m/s)
:v_entry => v_limit, # maximum entry speed (in m/s)
:v_exit => v_limit) # maximum exit speed (in m/s)
# list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated
s_exit = characteristicSection[:s_exit]
pointsOfInterest = Vector{Real}()
if haskey(path, :pointsOfInterest)
for POI in path[:pointsOfInterest]
if s_entry < POI && POI < s_exit
push!(pointsOfInterest, POI)
end
end
end
push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with
merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest))
return characteristicSection
end #function createCharacteristicSection
## define the intersection velocities between the characterisitc sections to secure braking behavior
function secureBrakingBehavior!(movingSection::Dict, a_braking::Real)
# this function limits the entry and exit velocity of the characteristic sections to secure that the train stops at the moving sections end

View File

@ -5,18 +5,22 @@
# __copyright__ = "2020-2022"
# __license__ = "ISC"
function createOutput(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict})
function createOutput(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict})
if settings.outputDetail == :running_time
output = movingSection[:t] # TODO: or use drivingCourse[end][:t]
elseif settings.outputDetail == :points_of_interest
# add points of interest
if haskey(path, :pointsOfInterest)
output = Vector{Dict}()
if !isempty(path.poi)
# for elem in 1:length(driving_course)
# end
output = Dict[]
POI = 1
i = 1
while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i]
if path[:pointsOfInterest][POI] == drivingCourse[i][:s]
while POI <= length(path.poi) && i <= drivingCourse[end][:i]
if path.poi[POI][:station] == drivingCourse[i][:s]
push!(output, drivingCourse[i])
POI = POI+1
end
@ -42,12 +46,12 @@ function createOutput(train::Dict, settings::Settings, path::Dict, movingSection
end
# add points of interest
if haskey(path, :pointsOfInterest)
if !isempty(path.poi)
pointsOfInterest = Vector{Dict}()
POI = 1
i = 1
while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i]
if path[:pointsOfInterest][POI] == drivingCourse[i][:s]
while POI <= length(path.poi) && i <= drivingCourse[end][:i]
if path.poi[POI] == drivingCourse[i][:s]
push!(pointsOfInterest, drivingCourse[i])
POI = POI+1
end
@ -67,7 +71,7 @@ function createOutput(train::Dict, settings::Settings, path::Dict, movingSection
end
#=
function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict})
function createOutputDict(train::Dict, settings::Settings, path::Path, movingSection::Dict, drivingCourse::Vector{Dict})
outputDict = Dict{Symbol,Any}()
merge!(outputDict, Dict(:train => train, :path => path, :settings => settings))
@ -82,12 +86,12 @@ function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSec
end
# add points of interest
if haskey(path, :pointsOfInterest)
if !isempty(path.poi)
pointsOfInterest = Vector{Dict}()
POI = 1
i = 1
while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i]
if path[:pointsOfInterest][POI] == drivingCourse[i][:s]
while POI <= length(path.poi) && i <= drivingCourse[end][:i]
if path.poi[POI] == drivingCourse[i][:s]
push!(pointsOfInterest, drivingCourse[i])
POI = POI+1
end

View File

@ -41,7 +41,9 @@ struct Settings
outputFormat = :julia_dict
outputDir = "."
## load from file
if file != "DEFAULT"
## JSON schema for YAML-file validation
schema = Schema("""{
"properties": {
@ -83,7 +85,7 @@ struct Settings
try
validate(schema, settings)
catch err
println("Could not load settings file $file.\n Format is not recognized - using default as fall back.")
println("Could not load settings file '$file'.\n Format is not recognized - using default as fall back.")
settings = Dict()
end
@ -104,18 +106,283 @@ struct Settings
end #struct Settings
"""
Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them.
"""
function checkAndSetInput!(train::Dict, path::Dict, settings::Settings)
checkAndSetTrain!(train)
checkAndSetPath!(path)
Path(file, type = :YAML)
if settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest)
throw(DomainError(settings.outputDetail, "INFO at checking the input for settings and path:\n
settings[:outputDetail] is 'points of interest' but the path does not for pointsOfInterest."))
Path is a datastruture for calculation context.
The function Path() will create a running path for the train.
# Example
```jldoctest
julia> my_path = Path("file.yaml") # will generate a path from a YAML file.
Path(variables)
```
"""
struct Path
name::String # a name or description of the path
id::String # a short string as identifier
uuid::UUID # a unique identifier
poi::Vector # a vector of triples with points along the path
sections::Vector # a vector of the characteristic sections
## constructor
function Path(file, type = :YAML)
## default values
name = ""
id = ""
uuid = UUIDs.uuid4()
poi = []
sections = []
## process flags
POI_PRESENT = false
## load from file
if type == :YAML
path = YAML.load(open(file))["path"]
## JSON schema for YAML-file validation
railtoolkit_schema = Schema("""{
"required": [ "name", "id", "characteristic_sections" ],
"properties": {
"characteristic_sections": {
"description": "",
"type": "array",
"minItems": 2,
"uniqueItems": true,
"items": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"description": "",
"prefixItems": [
{ "type": "number" },
{ "type": "number" },
{ "type": "number" }
]
}
},
"id": {
"description": "Identifier of the path",
"type": "string"
},
"name": {
"description": "Name of the path",
"type": "string"
},
"points_of_interest": {
"description": "",
"type": "array",
"uniqueItems": true,
"items": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"description": "",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": [ "front", "rear" ] }
]
}
},
"UUID": {
"description": "The unique identifier for a path",
"type": "string",
"format": "uuid"
}
}
}""")
try
validate(railtoolkit_schema, path)
catch err
error("Could not load path file '$file'.\n
YAML format is not recognized.
Currently supported: railtoolkit/schema (2022.04)")
end
return (train, path)
end #function checkAndSetInput!
## set the variables if they exist in "settings"
# required
name = path["name"]
id = path["id"]
tmp_sec = path["characteristic_sections"]
# optional
haskey(path, "UUID") ? uuid = parse(UUID, path["UUID"] ) : nothing
haskey(path, "points_of_interest") ? POI_PRESENT = true : nothing
haskey(path, "points_of_interest") ? tmp_points = path["points_of_interest"] : nothing
else
error("Unknown file type '$type'")
end #if type
## process characteristic sections
sort!(tmp_sec, by = x -> x[1])
for row in 2:length(tmp_sec)
s_start = tmp_sec[row-1][1] # first point of the section (in m)
s_end = tmp_sec[row][1] # first point of the next section (in m)
v_limit = tmp_sec[row-1][2]/3.6 # paths speed limt (in m/s)
f_Rp = tmp_sec[row-1][3] # specific path resistance of the section (in ‰)
section = Dict(:s_start => s_start,
:s_end => s_end,
:v_limit => v_limit,
:f_Rp => f_Rp)
push!(sections, section)
end #for row
# s_start in first entry defines the path's beginning
# s_end in last entry defines the path's ending
## process points of interest
if POI_PRESENT
sort!(tmp_points, by = x -> x[1])
for elem in tmp_points
station = elem[1] # first point of the section (in m)
label = elem[2] # paths speed limt (in m/s)
measure = elem[3] # specific path resistance of the section (in ‰)
point = Dict(:station => station,
:label => label,
:measure => measure)
push!(poi, point)
end #for elem
end #if !isempty(points)
new(name, id, uuid, poi, sections)
end #function Path
end #struct Path
"""
a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points
"""
function createDataPoint()
dataPoint = Dict(
:i => 0, # identifier and counter variable of the driving course
:behavior => "", # type of behavior section the data point is part of - see createBehaviorSection()
# a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter
:s => 0.0, # position (in m)
:Δs => 0.0, # step size (in m)
:t => 0.0, # point in time (in s)
:Δt => 0.0, # step size (in s)
:v => 0.0, # velocity (in m/s)
:Δv => 0.0, # step size (in m/s)
:a => 0.0, # acceleration (in m/s^2)
:W => 0.0, # mechanical work (in Ws)
:ΔW => 0.0, # mechanical work in this step (in Ws)
:E => 0.0, # energy consumption (in Ws)
:ΔE => 0.0, # energy consumption in this step (in Ws)
:F_T => 0.0, # tractive effort (in N)
:F_R => 0.0, # resisting force (in N)
:R_path => 0.0, # path resistance (in N)
:R_train => 0.0, # train resistance (in N)
:R_traction => 0.0, # traction unit resistance (in N)
:R_wagons => 0.0, # set of wagons resistance (in N)
:label => "" # a label for importend points
)
return dataPoint
end #function createDataPoint
"""
BehaviorSection() TODO!
"""
function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer)
BS= Dict(
:type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill"
:length => 0.0, # total length (in m)
:s_entry => s_entry, # first position (in m)
:s_exit => 0.0, # last position (in m)
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:v_entry => v_entry, # entry speed (in m/s)
:v_exit => 0.0, # exit speed (in m/s)
:dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point
)
return BS
end #function createBehaviorSection
## create a moving section containing characteristic sections
function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real)
# this function creates and returns a moving section dependent on the paths attributes
s_entry = path.sections[1][:s_start] # first position (in m)
s_exit = path.sections[end][:s_end] # last position (in m)
pathLength = s_exit - s_entry # total length (in m)
CSs=Vector{Dict}()
s_csStart=s_entry
csId=1
for row in 2:length(path.sections)
previousSection = path.sections[row-1]
currentSection = path.sections[row]
speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit)
pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp]
if speedLimitIsDifferent || pathResistanceIsDifferent
# 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp]
push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path))
s_csStart = currentSection[:s_start]
csId = csId+1
end #if
end #for
push!(CSs, createCharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path))
movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore
:length => pathLength, # total length (in m)
:s_entry => s_entry, # first position (in m)
:s_exit => s_exit, # last position (in m)
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:characteristicSections => CSs) # list of containing characteristic sections
return movingSection
end #function createMovingSection
## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections.
function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path)
# Create and return a characteristic section dependent on the paths attributes
characteristicSection= Dict(:id => id, # identifier
:s_entry => s_entry, # first position (in m)
:s_exit => section[:s_end], # last position (in m)
:length => section[:s_end] -s_entry, # total length (in m)
:r_path => section[:f_Rp], # path resistance (in ‰)
:behaviorSections => Dict(), # list of containing behavior sections
:t => 0.0, # total running time (in s)
:E => 0.0, # total energy consumption (in Ws)
:v_limit => v_limit, # speed limit (in m/s)
# initializing :v_entry, :v_peak and :v_exit with :v_limit
:v_peak => v_limit, # maximum reachable speed (in m/s)
:v_entry => v_limit, # maximum entry speed (in m/s)
:v_exit => v_limit) # maximum exit speed (in m/s)
# list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated
s_exit = characteristicSection[:s_exit]
##TODO: use a tuple with naming
pointsOfInterest = Tuple[]
# pointsOfInterest = Real[]
if !isempty(path.poi)
for POI in path.poi
s_poi = POI[:station]
if POI[:measure] == "rear"
s_poi -= s_trainLength
end
if s_entry < s_poi && s_poi < s_exit
push!(pointsOfInterest, (s_poi, POI[:label]) )
# push!(pointsOfInterest, s_poi )
end
end
end
push!(pointsOfInterest, (s_exit,"")) # s_exit has to be the last POI so that there will always be a POI to campare the current position with
# push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with
merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest))
return characteristicSection
end #function createCharacteristicSection
"""
Read the train information from a YAML file, save it in a train Dict and return it.
@ -174,7 +441,7 @@ function checkAndSetTrain!(train::Dict)
return train
end #function checkAndSetTrain!
function checkAndSetPath!(path::Dict)
function checkAndSetPath!(path::Path)
# check path information from input dictionary
checkAndSetString!(path, "path", :name, "")
@ -561,217 +828,220 @@ function getDefault_Δv_w(type::String) # coefficient for velocitiy differen
return Δv_w
end #function getDefault_Δv_w!
function checkAndSetSections!(path::Dict)
# check the section information
if haskey(path,:sections) && path[:sections]!=nothing
# TODO: check typeof(path[:sections]) == Dict
if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing)
println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." )
# function checkAndSetSections!(path::Path)
# # check the section information
# if haskey(path,:sections) && path.sections!=nothing
# # TODO: check typeof(path.sections) == Dict
# if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing)
# println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." )
elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." )
# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
# println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." )
elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." )
end
elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
# TODO: check typeof(path[:sections]) == Array
createSections!(path, :sectionStarts)
if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." )
end
elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# TODO: check typeof(path[:sections]) == Array
createSections!(path, :sectionStarts_kmh)
else
error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.")
section = Dict(:s_start => 0.0,
:s_end => 15.0,
:v_limit => 1000.0/3.6,
:f_Rp => 0.0)
merge!(path, Dict(:sections => [section]))
return path
end
sections = path[:sections]
checkedSections = []
increasing = false
decreasing = false
#TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections?
# check values for section==1
checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m", 0.0) # first point of the section (in m)
checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m", 0.0) # first point of the next section (in m)
checkAndSetPositiveNumber!(sections[1], "path[:sections][1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
checkAndSetRealNumber!(sections[1], "path[:sections][1]", :f_Rp, "", 0.0) # specific path resistance of the section (in ‰)
push!(checkedSections, sections[1])
if sections[1][:s_start] < sections[1][:s_end]
increasing = true
elseif sections[1][:s_start] > sections[1][:s_end]
decreasing = true
else
pop!(checkedSections)
println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
end
for sectionNr in 2:length(sections)
checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m)
# TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values
#if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real
# defaultEnd = sections[sectionNr+1][:s_start]
# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." )
# end
defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed!
checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m)
checkAndSetPositiveNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :f_Rp, "", 0.0) # specific path resistance of the section (in ‰)
# elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing
# # TODO: check typeof(path.sections) == Array
# createSections!(path, :sectionStarts)
push!(checkedSections, sections[sectionNr])
# if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." )
# end
# elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing
# # TODO: check typeof(path.sections) == Array
# createSections!(path, :sectionStarts_kmh)
# else
# error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.")
# section = Dict(:s_start => 0.0,
# :s_end => 15.0,
# :v_limit => 1000.0/3.6,
# :f_Rp => 0.0)
# merge!(path, Dict(:sections => [section]))
# return path
# end
# compare the section's start and end position
if sections[sectionNr][:s_start] < sections[sectionNr][:s_end]
increasing = true
elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end]
decreasing = true
else
pop!(checkedSections)
println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
end
if increasing && decreasing
error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.")
end
# sections = path.sections
# checkedSections = []
# increasing = false
# decreasing = false
# #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections?
# # check values for section==1
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_start, "m", 0.0) # first point of the section (in m)
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :s_end, "m", 0.0) # first point of the next section (in m)
# checkAndSetPositiveNumber!(sections[1], "path.sections[1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
# checkAndSetRealNumber!(sections[1], "path.sections[1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰)
# push!(checkedSections, sections[1])
# if sections[1][:s_start] < sections[1][:s_end]
# increasing = true
# elseif sections[1][:s_start] > sections[1][:s_end]
# decreasing = true
# else
# pop!(checkedSections)
# println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
# end
if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end]
error("ERROR at checking the input for the path[:sections]: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.")
# TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error?
end
end #for
# for sectionNr in 2:length(sections)
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m)
# # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values
# #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real
# # defaultEnd = sections[sectionNr+1][:s_start]
# #end
# defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed!
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m)
# checkAndSetPositiveNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s)
# checkAndSetRealNumber!(sections[sectionNr], "path.sections["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰)
return path
end #function checkAndSetSections!
# push!(checkedSections, sections[sectionNr])
function createSections!(path::Dict, key::Symbol)
# read the section starting positions and corresponding information
if key == :sectionStarts
sectionStartsArray = path[:sectionStarts]
conversionFactor = 1.0 # conversion factor between the units m/s and m/s
if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing
println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." )
end
elseif key == :sectionStarts_kmh
sectionStartsArray = path[:sectionStarts_kmh]
conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s
else
error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.")
end # if
# check if the array is correct and if elements of the array have the correct type and valid values
errorDetected = false
if length(sectionStartsArray)<2
error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].")
end
for row in 1:length(sectionStartsArray)
if length(sectionStartsArray[row])>=3
if length(sectionStartsArray[row])>3
println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.")
end
else
error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].")
end
if !(typeof(sectionStartsArray[row][1]) <: Real)
errorDetected=true
println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.")
end
if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0)
errorDetected=true
println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.")
end
if !(typeof(sectionStartsArray[row][3]) <: Real)
errorDetected=true
println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.")
end
end # for
if errorDetected
error("ERROR at checking the input for the path: The values of ",key," have to be corrected.")
end
# # compare the section's start and end position
# if sections[sectionNr][:s_start] < sections[sectionNr][:s_end]
# increasing = true
# elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end]
# decreasing = true
# else
# pop!(checkedSections)
# println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.")
# end
# if increasing && decreasing
# error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.")
# end
sections = []
for row in 2:length(sectionStartsArray)
s_start = sectionStartsArray[row-1][1] # first point of the section (in m)
s_end = sectionStartsArray[row][1] # first point of the next section (in m)
v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s)
f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰)
# if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end]
# error("ERROR at checking the input for the path.sections: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.")
# # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error?
# end
# end #for
section = Dict(:s_start => s_start,
:s_end => s_end,
:v_limit => v_limit,
:f_Rp => f_Rp)
push!(sections, section)
end # for
# s_start in first entry defines the path's beginning
# s_end in last entry defines the path's ending
# return path
# end #function checkAndSetSections!
merge!(path, Dict(:sections => sections))
return path
end #function createSections!
# function createSections!(path::Path, key::Symbol)
# # read the section starting positions and corresponding information
# if key == :sectionStarts
# sectionStartsArray = path[:sectionStarts]
# conversionFactor = 1.0 # conversion factor between the units m/s and m/s
function checkAndSetPOIs!(path::Dict)
# read the section starting positions and corresponding information
if haskey(path, :pointsOfInterest)
if path[:pointsOfInterest] != nothing
pointsOfInterest = path[:pointsOfInterest]
# if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing
# println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." )
# end
# elseif key == :sectionStarts_kmh
# sectionStartsArray = path[:sectionStarts_kmh]
# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s
# elseif key == :characteristic_sections
# sectionStartsArray = path[:characteristic_sections]
# conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s
# else
# error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.")
# end # if
sortingNeeded = false
errorDetected = false
for element in 1:length(pointsOfInterest)
if typeof(pointsOfInterest[element]) <: Real
if element > 1
if pointsOfInterest[element] < pointsOfInterest[element-1]
sortingNeeded = true
println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.")
end
end
else
errorDetected = true
println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.")
end
end # for
# # check if the array is correct and if elements of the array have the correct type and valid values
# errorDetected = false
# if length(sectionStartsArray)<2
# error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].")
# end
if errorDetected
error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.")
end
if sortingNeeded == true
sort!(pointsOfInterest)
end
# for row in 1:length(sectionStartsArray)
# if length(sectionStartsArray[row])>=3
# if length(sectionStartsArray[row])>3
# println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.")
# end
# else
# error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].")
# end
copiedPOIs = []
for element in 1:length(pointsOfInterest)
if element == 1
push!(copiedPOIs, pointsOfInterest[element])
elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1]
push!(copiedPOIs, pointsOfInterest[element])
end
end # for
path[:pointsOfInterest] = copiedPOIs
# if !(typeof(sectionStartsArray[row][1]) <: Real)
# errorDetected=true
# println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.")
# end
# if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0)
# errorDetected=true
# println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.")
# end
# if !(typeof(sectionStartsArray[row][3]) <: Real)
# errorDetected=true
# println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.")
# end
# end # for
# if errorDetected
# error("ERROR at checking the input for the path: The values of ",key," have to be corrected.")
# end
else
println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.")
delete!(path, :pointsOfInterest)
end
end
return path
end #function checkAndSetPOIs!
# sections = []
# for row in 2:length(sectionStartsArray)
# s_start = sectionStartsArray[row-1][1] # first point of the section (in m)
# s_end = sectionStartsArray[row][1] # first point of the next section (in m)
# v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s)
# f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰)
# section = Dict(:s_start => s_start,
# :s_end => s_end,
# :v_limit => v_limit,
# :f_Rp => f_Rp)
# push!(sections, section)
# end # for
# # s_start in first entry defines the path's beginning
# # s_end in last entry defines the path's ending
# merge!(path, Dict(:sections => sections))
# return path
# end #function createSections!
# function checkAndSetPOIs!(path::Path)
# # read the section starting positions and corresponding information
# if haskey(path, :pointsOfInterest)
# # if path.poi != nothing
# pointsOfInterest = path[:points_of_interest]
# sortingNeeded = false
# errorDetected = false
# for element in 1:length(pointsOfInterest)
# if typeof(pointsOfInterest[element]) <: Real
# if element > 1
# if pointsOfInterest[element] < pointsOfInterest[element-1]
# sortingNeeded = true
# println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.")
# end
# end
# else
# errorDetected = true
# println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.")
# end
# end # for
# if errorDetected
# error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.")
# end
# if sortingNeeded == true
# sort!(pointsOfInterest)
# end
# copiedPOIs = []
# for element in 1:length(pointsOfInterest)
# if element == 1
# push!(copiedPOIs, pointsOfInterest[element])
# elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1]
# push!(copiedPOIs, pointsOfInterest[element])
# end
# end # for
# path[:points_of_interest ] = copiedPOIs
# # else
# # println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.")
# # delete!(path, :points_of_interest)
# # end
# end
# return path
# end #function checkAndSetPOIs!
#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool
function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool

View File

@ -2,8 +2,18 @@
---
path:
name: "10 km, no gradient, 160 km/h"
pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m
sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰]
sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰]
- [0, 160, 0]
- [10000, 160, 0]
id: const
UUID: 23ff336e-9b9a-4535-bdb6-9db488b10945
points_of_interest:
# [ station in m, name, front or rear ]
- [ 999.00, point_1, front ]
- [ 2000.00, point_2, front ]
- [ 3333.30, point_3, rear ]
- [ 5000.00, point_4, front ]
- [ 7777.00, point_5, front ]
- [ 9000.00, point_6, front ]
- [ 9500.95, point_7, front ]
characteristic_sections:
# [ station in m, speed limit in km/h, resistance in ‰ ]
- [ 0.0, 160, 0.00 ]
- [ 10000.0, 160, 0.00 ]

View File

@ -2,25 +2,31 @@
---
path:
name: "'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5"
# source: https://www.railml.org/en/user/exampledata.html -> "Real world railway examples from professional tools" -> "East Saxony railway network by FBS" -> "Ostsachsen_V220.railml" -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5
sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰]
sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰]
- [0.0, 40, 0]
- [318.0, 40, 2]
- [399.0, 40, -3]
- [500.0, 40, 0]
- [579.0, 40, 1]
id: realworld
UUID: 2b31a0c5-85bc-4721-b7e0-66f9df95f7b6
# source: https://www.railml.org/en/user/exampledata.html
# -> "Real world railway examples from professional tools"
# -> "East Saxony railway network by FBS"
# -> "Ostsachsen_V220.railml"
# -> 'infra_Ostsachsen': track id='tr_80.6212_2' name='DG-DN' -> spp_5
characteristic_sections:
# [ s in m, v_limit in km/h, f_Rp in ‰ ]
- [ 0.0, 40, 0.0 ]
- [ 318.0, 40, 2.0 ]
- [ 399.0, 40, -3.0 ]
- [ 500.0, 40, 0.0 ]
- [ 579.0, 40, 1.0 ]
- [ 784.0, 40, 5.3 ]
- [868.0, 40, 20]
- [ 868.0, 40, 20.0 ]
- [ 1082.0, 40, 16.1 ]
- [ 1287.0, 40, 18.1 ]
- [ 1800.0, 110, 18.1 ]
- [ 2242.0, 110, 15.4 ]
- [3295.0, 110, 11]
- [ 3295.0, 110, 11.0 ]
- [ 3880.0, 110, 11.1 ]
- [ 4680.0, 45, 11.1 ]
- [ 4686.0, 90, 11.1 ]
- [6122.0, 90, 0]
- [ 6122.0, 90, 0.0 ]
- [ 6487.0, 90, 1.5 ]
- [ 6588.0, 70, 1.5 ]
- [ 6589.0, 70, 2.4 ]
@ -28,12 +34,12 @@ path:
- [ 6723.0, 150, 1.3 ]
- [ 6928.0, 160, 1.3 ]
- [ 7030.0, 160, 7.5 ]
- [7300.0, 160, 8]
- [7552.0, 160, 5]
- [ 7300.0, 160, 8.0 ]
- [ 7552.0, 160, 5.0 ]
- [ 7675.0, 160, 6.7 ]
- [ 7800.0, 160, 7.4 ]
- [7920.0, 160, 7]
- [8020.0, 140, 7]
- [ 7920.0, 160, 7.0 ]
- [ 8020.0, 140, 7.0 ]
- [ 8100.0, 140, 6.3 ]
- [ 8168.0, 140, 7.4 ]
- [ 8226.0, 140, 8.4 ]
@ -42,10 +48,10 @@ path:
- [ 8600.0, 150, 7.8 ]
- [ 8900.0, 150, 6.7 ]
- [ 9100.0, 150, 7.8 ]
- [9600.0, 150, 8]
- [ 9600.0, 150, 8.0 ]
- [ 9845.0, 150, 7.3 ]
- [ 10005.0, 160, 7.3 ]
- [10600.0, 160, 6]
- [ 10600.0, 160, 6.0 ]
- [ 10748.0, 160, 7.3 ]
- [ 11100.0, 160, 4.5 ]
- [ 11280.0, 160, 3.6 ]
@ -62,7 +68,7 @@ path:
- [ 14764.0, 160, -2.8 ]
- [ 15000.0, 160, -3.3 ]
- [ 15500.0, 160, -0.9 ]
- [16000.0, 160, 0]
- [ 16000.0, 160, 0.0 ]
- [ 16470.0, 160, 1.2 ]
- [ 16572.0, 160, 2.2 ]
- [ 16700.0, 160, 3.8 ]
@ -73,21 +79,21 @@ path:
- [ 17406.0, 160, 3.4 ]
- [ 17727.0, 150, 3.4 ]
- [ 17807.0, 150, 4.6 ]
- [18049.0, 150, 3]
- [18210.0, 140, 3]
- [ 18049.0, 150, 3.0 ]
- [ 18210.0, 140, 3.0 ]
- [ 18300.0, 140, 4.6 ]
- [ 18680.0, 140, 3.2 ]
- [ 18761.0, 150, 3.2 ]
- [ 19047.0, 150, 5.1 ]
- [19305.0, 150, 3]
- [19406.0, 160, 3]
- [ 19305.0, 150, 3.0 ]
- [ 19406.0, 160, 3.0 ]
- [ 19414.0, 160, -0.3 ]
- [19900.0, 160, 3]
- [ 19900.0, 160, 3.0 ]
- [ 20150.0, 160, 1.8 ]
- [20470.0, 160, -4]
- [ 20470.0, 160, -4.0 ]
- [ 20940.0, 160, -3.6 ]
- [ 21150.0, 160, -1.5 ]
- [21390.0, 160, 0]
- [ 21390.0, 160, 0.0 ]
- [ 21702.0, 160, 1.5 ]
- [ 22188.0, 150, 1.5 ]
- [ 22294.0, 150, 1.8 ]
@ -96,7 +102,7 @@ path:
- [ 22900.0, 160, 4.6 ]
- [ 23542.0, 160, 0.2 ]
- [ 23736.0, 160, 0.9 ]
- [24124.0, 160, 7]
- [ 24124.0, 160, 7.0 ]
- [ 24918.0, 160, 7.6 ]
- [ 25100.0, 150, 7.1 ]
- [ 25580.0, 150, 7.4 ]
@ -111,7 +117,7 @@ path:
- [ 27310.0, 160, 3.5 ]
- [ 27595.0, 160, 3.4 ]
- [ 28530.0, 160, 4.6 ]
- [29115.0, 160, 0]
- [ 29115.0, 160, 0.0 ]
- [ 29700.0, 160, -5.2 ]
- [ 30055.0, 120, -4.3 ]
- [ 30301.0, 120, -6.2 ]
@ -124,7 +130,7 @@ path:
- [ 31795.0, 120, 4.1 ]
- [ 32010.0, 120, 3.2 ]
- [ 32138.0, 130, 3.2 ]
- [32365.0, 130, -4]
- [ 32365.0, 130, -4.0 ]
- [ 33000.0, 130, 6.1 ]
- [ 33426.0, 160, 6.1 ]
- [ 33907.0, 160, 7.1 ]
@ -136,15 +142,15 @@ path:
- [ 35173.0, 150, 5.6 ]
- [ 35400.0, 150, -0.2 ]
- [ 35597.0, 160, -0.2 ]
- [35900.0, 160, 0]
- [ 35900.0, 160, 0.0 ]
- [ 36700.0, 160, 3.9 ]
- [36938.0, 160, 0]
- [ 36938.0, 160, 0.0 ]
- [ 37700.0, 160, -0.3 ]
- [ 37978.0, 150, -5.6 ]
- [ 38063.0, 150, -1.9 ]
- [ 38141.0, 150, -3.1 ]
- [38210.0, 150, 0]
- [38406.0, 150, -7]
- [ 38210.0, 150, 0.0 ]
- [ 38406.0, 150, -7.0 ]
- [ 38900.0, 150, -7.5 ]
- [ 39200.0, 150, -8.7 ]
- [ 39298.0, 150, -6.4 ]
@ -165,10 +171,10 @@ path:
- [ 43388.0, 160, -0.6 ]
- [ 43700.0, 160, 0.8 ]
- [ 44030.0, 160, 2.7 ]
- [44430.0, 160, 0]
- [ 44430.0, 160, 0.0 ]
- [ 44708.0, 160, -7.2 ]
- [ 45477.0, 160, -7.8 ]
- [45890.0, 160, -1]
- [ 45890.0, 160, -1.0 ]
- [ 46562.0, 160, -1.6 ]
- [ 47000.0, 160, -6.9 ]
- [ 47500.0, 160, -7.7 ]
@ -195,15 +201,15 @@ path:
- [ 54855.0, 140, -6.2 ]
- [ 55307.0, 140, -0.3 ]
- [ 55651.0, 140, -1.2 ]
- [55788.0, 140, 0]
- [55918.0, 100, 0]
- [56433.0, 150, 0]
- [ 55788.0, 140, 0.0 ]
- [ 55918.0, 100, 0.0 ]
- [ 56433.0, 150, 0.0 ]
- [ 56560.0, 150, -2.1 ]
- [ 56624.0, 150, -6.7 ]
- [ 57012.0, 150, 1.3 ]
- [ 57260.0, 150, 6.6 ]
- [ 57800.0, 150, 5.3 ]
- [57987.0, 150, 0]
- [ 57987.0, 150, 0.0 ]
- [ 57990.0, 150, 7.6 ]
- [ 58321.0, 150, 6.4 ]
- [ 59090.0, 150, 6.9 ]
@ -212,7 +218,7 @@ path:
- [ 60683.0, 150, 4.1 ]
- [ 61156.0, 150, 2.3 ]
- [ 61181.0, 130, 2.3 ]
- [61325.0, 130, 0]
- [ 61325.0, 130, 0.0 ]
- [ 61605.0, 130, 7.3 ]
- [ 62108.0, 150, 7.3 ]
- [ 62246.0, 150, 6.6 ]
@ -222,22 +228,22 @@ path:
- [ 63802.0, 150, 4.6 ]
- [ 64344.0, 150, 0.2 ]
- [ 64932.0, 150, -0.6 ]
- [65100.0, 150, 0]
- [ 65100.0, 150, 0.0 ]
- [ 65690.0, 150, 1.8 ]
- [ 65878.0, 150, 2.5 ]
- [66266.0, 150, -1]
- [ 66266.0, 150, -1.0 ]
- [ 66339.0, 150, 6.3 ]
- [ 66448.0, 160, 6.3 ]
- [66587.0, 160, 0]
- [ 66587.0, 160, 0.0 ]
- [ 66856.0, 160, 3.2 ]
- [ 67480.0, 160, 3.6 ]
- [ 67697.0, 160, 2.2 ]
- [67800.0, 160, 6]
- [67851.0, 130, 6]
- [ 67800.0, 160, 6.0 ]
- [ 67851.0, 130, 6.0 ]
- [ 68027.0, 130, 2.7 ]
- [ 68172.0, 130, 0.6 ]
- [ 68328.0, 130, 2.5 ]
- [68357.0, 130, 0]
- [ 68357.0, 130, 0.0 ]
- [ 68479.0, 130, 7.1 ]
- [ 68783.0, 130, 7.4 ]
- [ 69056.0, 150, 7.4 ]
@ -245,7 +251,7 @@ path:
- [ 69741.0, 160, 6.8 ]
- [ 69900.0, 160, 6.6 ]
- [ 70757.0, 160, 3.6 ]
- [71384.0, 160, 6]
- [ 71384.0, 160, 6.0 ]
- [ 71568.0, 160, 7.1 ]
- [ 71800.0, 160, 7.4 ]
- [ 72100.0, 160, 7.3 ]
@ -253,7 +259,7 @@ path:
- [ 74317.0, 140, 7.3 ]
- [ 74448.0, 140, 1.5 ]
- [ 74590.0, 140, -1.5 ]
- [74620.0, 140, -5]
- [ 74620.0, 140, -5.0 ]
- [ 74950.0, 140, -4.3 ]
- [ 75100.0, 140, -1.8 ]
- [ 75154.0, 130, -1.8 ]
@ -262,22 +268,22 @@ path:
- [ 76062.0, 100, -5.6 ]
- [ 76100.0, 100, -6.4 ]
- [ 76350.0, 100, -5.7 ]
- [76476.0, 100, -7]
- [ 76476.0, 100, -7.0 ]
- [ 76600.0, 100, -6.4 ]
- [ 76601.0, 90, -6.4 ]
- [ 76726.0, 90, -6.2 ]
- [ 77256.0, 90, -2.1 ]
- [ 77285.0, 80, -2.1 ]
- [77299.0, 80, -14]
- [ 77299.0, 80, -14.0 ]
- [ 77331.0, 80, -1.6 ]
- [ 77379.0, 90, -1.6 ]
- [ 77425.0, 110, -1.6 ]
- [ 77455.0, 110, -4.9 ]
- [77498.0, 110, 0]
- [77505.0, 160, 0]
- [ 77498.0, 110, 0.0 ]
- [ 77505.0, 160, 0.0 ]
- [ 77555.0, 160, -5.6 ]
- [77662.0, 160, 0]
- [78085.0, 160, -4]
- [ 77662.0, 160, 0.0 ]
- [ 78085.0, 160, -4.0 ]
- [ 78223.0, 160, -6.8 ]
- [ 78337.0, 130, -6.8 ]
- [ 78856.0, 130, -6.6 ]
@ -289,20 +295,20 @@ path:
- [ 81300.0, 150, -4.8 ]
- [ 81634.0, 110, -4.8 ]
- [ 81943.0, 110, -5.4 ]
- [82166.0, 110, 0]
- [ 82166.0, 110, 0.0 ]
- [ 82408.0, 110, 4.8 ]
- [ 82790.0, 110, 5.9 ]
- [ 83137.0, 120, 5.9 ]
- [83300.0, 120, 0]
- [83519.0, 150, 0]
- [ 83300.0, 120, 0.0 ]
- [ 83519.0, 150, 0.0 ]
- [ 83597.0, 150, -7.8 ]
- [ 83827.0, 150, -8.1 ]
- [ 84150.0, 150, -7.2 ]
- [84391.0, 150, 0]
- [84966.0, 150, -4]
- [ 84391.0, 150, 0.0 ]
- [ 84966.0, 150, -4.0 ]
- [ 85529.0, 130, -2.3 ]
- [85589.0, 130, -4]
- [86081.0, 130, 0]
- [ 85589.0, 130, -4.0 ]
- [ 86081.0, 130, 0.0 ]
- [ 86514.0, 130, 7.8 ]
- [ 86577.0, 120, 7.8 ]
- [ 87554.0, 90, 7.8 ]
@ -315,12 +321,12 @@ path:
- [ 88450.0, 160, 6.7 ]
- [ 89050.0, 160, 7.4 ]
- [ 89350.0, 160, -7.1 ]
- [90365.0, 160, 0]
- [ 90365.0, 160, 0.0 ]
- [ 90700.0, 160, -7.3 ]
- [ 92000.0, 160, -7.9 ]
- [92166.0, 160, -4]
- [ 92166.0, 160, -4.0 ]
- [ 92460.0, 160, -5.4 ]
- [93330.0, 160, 0]
- [ 93330.0, 160, 0.0 ]
- [ 93901.0, 160, 0.7 ]
- [ 94156.0, 160, 5.2 ]
- [ 94440.0, 160, -2.8 ]
@ -332,14 +338,14 @@ path:
- [ 96500.0, 160, -4.4 ]
- [ 96700.0, 160, -5.6 ]
- [ 97000.0, 160, -4.6 ]
- [97590.0, 160, 0]
- [97858.0, 120, 0]
- [ 97590.0, 160, 0.0 ]
- [ 97858.0, 120, 0.0 ]
- [ 98224.0, 120, -1.5 ]
- [98264.0, 120, 0]
- [ 98264.0, 120, 0.0 ]
- [ 98577.0, 120, 7.5 ]
- [98738.0, 120, 0]
- [99055.0, 130, 0]
- [99427.0, 130, -2]
- [ 98738.0, 120, 0.0 ]
- [ 99055.0, 130, 0.0 ]
- [ 99427.0, 130, -2.0 ]
- [ 99610.0, 130, -3.1 ]
- [ 99906.0, 120, -3.1 ]
- [ 99980.0, 120, -1.3 ]
@ -351,4 +357,4 @@ path:
- [101332.0, 100, -6.3 ]
- [101365.0, 100, -2.4 ]
- [101551.0, 110, -2.4 ]
- [101800.0, 110, 0]
- [101800.0, 110, 0.0 ]

View File

@ -2,18 +2,26 @@
---
path:
name: "10 km, different gradient, 160 km/h"
pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m
sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰]
sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰]
- [0, 160, 0]
- [1000, 160, 1]
- [2000, 160, 2]
- [3000, 160, 5]
- [4000, 160, -3]
- [5000, 160, 5]
- [6000, 160, -10]
- [7000, 160, 15]
- [8000, 160, -10]
- [8500, 160, 20]
- [9000, 160, 0]
- [10000, 160, 0]
id: slope
UUID: ffd243a9-0223-4210-8c7d-e6c90fde70d3
points_of_interest:
# [ station in m, name, front or rear ]
- [ 850.00, view_point_1, front ]
- [ 1000.00, distant_signal_1, front ]
- [ 2000.00, main_signal_1, front ]
- [ 9000.00, main_signal_3, front ]
- [ 9050.00, clearing_point_1, rear ]
characteristic_sections:
# [ station in m, speed limit in km/h, resistance in ‰ ]
- [ 0.0, 160, 0.00 ]
- [ 1000.0, 160, 1.00 ]
- [ 2000.0, 160, 2.00 ]
- [ 3000.0, 160, 5.00 ]
- [ 4000.0, 160, -3.00 ]
- [ 5000.0, 160, 5.00 ]
- [ 6000.0, 160, -10.00 ]
- [ 7000.0, 160, 15.00 ]
- [ 8000.0, 160, -10.00 ]
- [ 8500.0, 160, 20.00 ]
- [ 9000.0, 160, 0.00 ]
- [ 10000.0, 160, 0.00 ]

View File

@ -2,16 +2,26 @@
---
path:
name: "10 km, no gradient, different speed limits"
pointsOfInterest: [999, 2000, 3333.3, 5000, 7777, 9000, 9500.95] # points of interest: positions in m
sectionStarts: # with path speed limt (in m/s) # [s in m, v_limit in m/s, f_Rp in ‰]
sectionStarts_kmh: # with path speed limt (in km/h) # [s in m, v_limit in km/h, f_Rp in ‰]
- [0, 160, 0.0]
- [3000, 60, 0.0]
- [4000, 160, 0.0]
- [5000, 60, 0.0]
- [6000, 160, 0.0]
- [6500, 60, 0.0]
- [6700, 65, 0.0]
- [6800, 70, 0.0]
- [7000, 120.00, 0]
- [10000, 160.00, 0.0]
id: speed
UUID: 401b8ce7-fa75-4576-8a4a-43be2eb55e50
points_of_interest:
# [ station in m, name, front or rear ]
- [ 999.00, point_1, front ]
- [ 2000.00, point_2, front ]
- [ 3333.30, point_3, rear ]
- [ 5000.00, point_4, front ]
- [ 7777.00, point_5, front ]
- [ 9000.00, point_6, front ]
- [ 9500.95, point_7, front ]
characteristic_sections:
# [ station in m, speed limit in km/h, resistance in ‰ ]
- [ 0.0, 160, 0.00 ]
- [ 3000.0, 60, 0.00 ]
- [ 4000.0, 160, 0.00 ]
- [ 5000.0, 60, 0.00 ]
- [ 6000.0, 160, 0.00 ]
- [ 6500.0, 60, 0.00 ]
- [ 6700.0, 65, 0.00 ]
- [ 6800.0, 70, 0.00 ]
- [ 7000.0, 120, 0.00 ]
- [ 10000.0, 160, 0.00 ]

View File

@ -1,9 +0,0 @@
%YAML 1.2
---
settings:
# default settings for the calculation
stepSize: 5 # step size, unit depends on stepVariable - distance in meter, time in seconds and velocity in meter/second.
approxLevel: 6 # value for approximation; used when rounding or interating
outputDetail: "running_time" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything"
outputFormat: "julia_dict" # output as "julia_dict" or as "csv"
outputDir: "." # used if other outputFormat than "julia dict"

View File

@ -0,0 +1,4 @@
%YAML 1.2
---
settings:
outputDetail: "everything" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything"

View File

@ -0,0 +1,4 @@
%YAML 1.2
---
settings:
outputDetail: "points_of_interest" # single value "running_time", array of "points_of_interest",complete array "driving_course", or dict() "everything"

View File

@ -7,63 +7,83 @@
using TrainRun, Test
paths=Dict()
push!(paths, "const" => TrainRun.importFromYaml(:path, "test/data/paths/const.yaml"))
push!(paths, "slope" => TrainRun.importFromYaml(:path, "test/data/paths/slope.yaml"))
push!(paths, "speed" => TrainRun.importFromYaml(:path, "test/data/paths/speed.yaml"))
push!(paths, "realworld" => TrainRun.importFromYaml(:path, "test/data/paths/realworld.yaml"))
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"))
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"))
paths = Dict()
settings = Dict()
@testset "TrainRun.jl" begin
@testset "load data" begin
println("testing load train data")
push!(trains, :freight => @time TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml"))
push!(trains, :local => @time TrainRun.importFromYaml(:train, "test/data/trains/local.yaml"))
push!(trains, :longdistance => @time TrainRun.importFromYaml(:train, "test/data/trains/longdistance.yaml"))
println("testing load path data")
push!(paths, :const => @time Path("test/data/paths/const.yaml"))
push!(paths, :slope => @time Path("test/data/paths/slope.yaml"))
push!(paths, :speed => @time Path("test/data/paths/speed.yaml"))
push!(paths, :realworld => @time Path("test/data/paths/realworld.yaml"))
println("testing load settings data")
push!(settings, "default" => @time Settings())
push!(settings, "poi" => @time Settings("test/data/settings/points_of_interest.yaml"))
push!(settings, "drivingcourse" => @time Settings("test/data/settings/driving_course.yaml"))
push!(settings, "everything" => @time Settings("test/data/settings/everything.yaml"))
push!(settings, "strip" => @time Settings("test/data/settings/strip.yaml"))
push!(settings, "time" => @time Settings("test/data/settings/time.yaml"))
push!(settings, "timestrip" => @time Settings("test/data/settings/time_strip.yaml"))
push!(settings, "velocity" => @time Settings("test/data/settings/velocity.yaml"))
push!(settings, "csvexport" => @time Settings("test/data/settings/csv_export.yaml"))
@test typeof(first(paths)[2]) == Path
@test typeof(first(settings)[2]) == Settings
end
println("====================")
tests = Base.Iterators.product(trains,paths)
## routine to generate the anticipated Dict()
# anticipated = Dict()
# for test in tests
# println(test[1][1],"-",test[2][1])
# result = @time trainrun(test[1][2],test[2][2])
# push!(anticipated, Symbol(String(test[1][1]) * "_" * String(test[2][1])) => result )
# end
anticipated = Dict(
:default => Dict(
:longdistance_speed => 499.96109564970516,
:freight_slope => 831.4768274141168,
:local_slope => 396.99313307033276,
:longdistance_const => 328.83479381353095,
:freight_realworld => 8971.50124080998,
:longdistance_slope => 329.22915822053164,
:freight_const => 727.7969403041934,
:longdistance_realworld => 2900.1198723158523,
:local_speed => 524.3948201513945,
:local_realworld => 3443.917823618831,
:freight_speed => 733.2610572579886,
:local_const => 392.7234008268302
)
)
@testset "function trainrun()" begin
@testset "Default settings" begin
@test typeof(Settings()) == Settings
@testset "const path" begin
path = TrainRun.importFromYaml(:path, "test/data/paths/const.yaml")
@test typeof(path) == Dict{Any,Any}
@testset "freight train - const path" begin
train = TrainRun.importFromYaml(:train, "test/data/trains/freight.yaml")
data = trainRun(train, path)
expected = 727.796900196972
for test in tests
test_name = String(test[1][1]) * "_" * String(test[2][1])
println("testing $test_name")
@time result = trainrun(test[1][2],test[2][2])
expected = anticipated[:default][Symbol(test_name)]
# compare result to test data set
@test isapprox(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)
@test isapprox(result, expected, atol=0.01)
println("--------------------")
end
end
end
println("====================")
end