Compare commits

...

26 Commits

Author SHA1 Message Date
Martin Scheidt 25278d63de updated CHANGELOG.md for new version 2022-08-29 14:45:00 +02:00
Martin Scheidt 31befe815d
Merge pull request #23 from railtoolkit/development_max
cleanup of development max
2022-08-29 14:27:13 +02:00
Max Kannenberg 8b3d16517b Update changelog 2022-08-29 12:52:25 +02:00
Max Kannenberg 804f797b08 Fix typing error (change time to :time) 2022-08-24 10:08:20 +02:00
Max Kannenberg a7fe8db1c2 Refactor setting the state flag :endOfCsReached 2022-08-24 10:07:30 +02:00
Max Kannenberg bc02e96a6c Access elements from 'points of interests' by name instead of by index 2022-08-22 19:07:49 +02:00
Max Kannenberg 8c286b0d2b Change type of a point of interest from Tuple to NamedTuple 2022-08-19 19:51:02 +02:00
Max Kannenberg 94839a28c0 Remove key :i from SupportPoint dictionary 2022-08-18 13:44:57 +02:00
Max Kannenberg 4a047f4cf2 Remove CharacteristicSections as return value from function 'calculateMinimumRunningTime' 2022-08-17 22:30:32 +02:00
Max Kannenberg 4d44674d21 Remove key :id from CharacteristicSection dictionary 2022-08-17 22:26:46 +02:00
Max Kannenberg 548d46b6c4 Remove the key :v_entry from CharacteristicSection dictionary 2022-08-17 15:22:10 +02:00
Max Kannenberg b421fbec5a Remove the redundant function 'secureAcceleratingBehavior' 2022-08-17 12:58:53 +02:00
Max Kannenberg 6af0912359 Rename 'current speed limit' to 'lowest speed limit' 2022-08-17 12:18:27 +02:00
Max Kannenberg f11e64b8b8 Remove key :v_peak from CharacteristicSection dictionary 2022-08-16 23:23:02 +02:00
Max Kannenberg c4d8b2c79c Remove key :length from CharacteristicSection dictionary 2022-08-16 12:25:50 +02:00
Max Kannenberg 3626f46df9 Fix typing error (change :speed to :velocity) 2022-08-12 17:52:35 +02:00
Max Kannenberg 395b7eb11c Add rounding up at the CS's end if difference is beyond accuracy level 2022-08-12 13:24:27 +02:00
Max Kannenberg 442a342e84 Fix rounding error due to braking calculation 2022-08-11 16:54:38 +02:00
Martin Scheidt b8578ae49c
Merge pull request #22 from railtoolkit/development_max
Development max
2022-08-09 15:43:57 +02:00
Max Kannenberg cbd4e7f97f Round output data depending on the settings' approximation level 2022-08-08 16:12:08 +02:00
Max Kannenberg 5e33e62a79 Change output for outputDetail=:points_of_interest for a path without POI 2022-08-08 16:10:38 +02:00
Max Kannenberg acf8cd0c3b Remove the Dictionary for the behavior sections 2022-07-13 00:15:57 +02:00
Max Kannenberg 99a07094fc Remove the Dictionary for the moving section 2022-07-12 16:59:45 +02:00
Max Kannenberg b7e0f21ffb Remove step sizes from 'SupportPoint' 2022-07-05 21:42:19 +02:00
Max Kannenberg a3a68e5553 Add output detail 'data_points' with starting points of the driving modes 2022-07-05 21:41:41 +02:00
Max Kannenberg 892b84251a Rename 'data points' to 'support points' 2022-06-22 11:11:44 +02:00
12 changed files with 573 additions and 872 deletions

View File

@ -1,8 +1,7 @@
name: "continuous integration test"
on:
push:
branches:
- main
branches: [ main, development ]
paths-ignore:
- 'CHANGELOG.md'
- 'CITATION.cff'
@ -16,6 +15,7 @@ on:
- '.github/workflows/TagBot.yml'
- '.github/zenodo/*'
pull_request:
branches: [ main, development ]
paths-ignore:
- 'CHANGELOG.md'
- 'CITATION.cff'

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
version:
description: Version to register or component to bump
description: "Version to register or component to bump (without leading 'v' e.g. '1.0.1')"
required: true
jobs:

View File

@ -10,6 +10,25 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security.
## [Unreleased]
## Version [1.0.2] 2022-09-01
### Added
* output alternative with starting points of the driving modes
### Changed
* renamed data points into 'support points'
* reduced number of decimal places of output data
* replace v_peak by the existing v_limit
* changed type of a point of interest from Tuple to NamedTuple
### Removed
* dictionary MovingSection
* redundant keys from the dictionary CharacteristicSection
* dictionary BehaviorSection
* redundant keys from the dictionary SupportPoint
* function secureAcceleratingBehavior()
## Version [1.0.1] 2022-06-05
* automated Julia package registration
@ -192,7 +211,8 @@ Modules and variables were renamed.
Proof of concept and master thesis submission.
[Unreleased]: https://github.com/railtoolkit/TrainRuns.jl/compare/v1.0.1...main
[Unreleased]: https://github.com/railtoolkit/TrainRuns.jl/compare/v1.0.2...main
[1.0.2]: https://github.com/railtoolkit/TrainRuns.jl/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/railtoolkit/TrainRuns.jl/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.8...v1.0.0
[0.8]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.7...v0.8
@ -208,4 +228,4 @@ Proof of concept and master thesis submission.
[0.4]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.3...v0.4
[0.3]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.2...v0.3
[0.2]: https://github.com/railtoolkit/TrainRuns.jl/compare/v0.1...v0.2
[0.1]: https://github.com/railtoolkit/TrainRuns.jl/releases/tag/v0.1
[0.1]: https://github.com/railtoolkit/TrainRuns.jl/releases/tag/v0.1

View File

@ -46,15 +46,15 @@ xxx.xx # in seconds
"""
function trainrun(train::Train, path::Path, settings=Settings()::Settings)
# prepare the input data
movingSection = determineCharacteristics(path, train, settings)
# settings.outputDetail == :verbose && println("The moving section has been prepared.")
(characteristicSections, pointsOfInterest) = determineCharacteristics(path, train, settings)
# TODO settings.outputDetail == :verbose && println("The characteristics haven been determined.")
# calculate the train run for oparation mode "minimum running time"
(movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train)
# settings.outputDetail == :verbose && println("The driving course for the shortest running time has been calculated.")
# calculate the train run with the minimum running time
drivingCourse = calculateMinimumRunningTime(characteristicSections, settings, train)
# TODO settings.outputDetail == :verbose && println("The driving course for the shortest running time has been calculated.")
# accumulate data and create an output dictionary
output = createOutput(settings, drivingCourse, movingSection[:pointsOfInterest])
output = createOutput(settings, drivingCourse, pointsOfInterest)
return output
end # function trainrun

File diff suppressed because it is too large Load Diff

View File

@ -7,106 +7,102 @@
# 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::Train)
CSs::Vector{Dict} = movingSection[:characteristicSections]
if settings.massModel == :homogeneous_strip && settings.stepVariable == speed
println("WARNING: ! ! ! TrainRuns.jl doesn't work reliably for the mass model homogeneous strip with step size v in m/s. The calculation time can be extremely high when calcutlating paths with steep gradients ! ! !")
end
startingPoint=DataPoint()
startingPoint[:i]=1
startingPoint[:s]=CSs[1][:s_entry]
function calculateMinimumRunningTime(CSs::Vector{Dict}, settings::Settings, train::Train)
startingPoint = SupportPoint()
startingPoint[:s] = CSs[1][:s_entry]
calculateForces!(startingPoint, CSs, 1, "default", train, settings.massModel) # traction effort and resisting forces (in N)
drivingCourse::Vector{Dict} = [startingPoint] # List of data points
drivingCourse::Vector{Dict} = [startingPoint] # List of support points
for csId in 1:length(CSs)
CS = CSs[csId]
# for testing
if drivingCourse[end][:s] != CS[:s_entry]
println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry])
end
if drivingCourse[end][:v] > CS[:v_entry]
println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry])
end
# determine the different flags for switching between the states for creatinge moving phases
# determine the different flags for switching between the states for creating moving phases
s_braking = brakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking, settings.approxLevel)
calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N)
calculateForces!(drivingCourse[end], CSs, csId, "default", train, settings.massModel) # tractive effort and resisting forces (in N)
previousSpeedLimitReached = false
stateFlags = Dict(:endOfCSReached => drivingCourse[end][:s] > CS[:s_exit],
:brakingStartReached => drivingCourse[end][:s] + s_braking == CS[:s_exit],
:tractionDeficit => drivingCourse[end][:F_T] < drivingCourse[end][:F_R], # or add another flag for equal forces?
:resistingForceNegative => drivingCourse[end][:F_R] < 0.0,
:previousSpeedLimitReached => false, #speedLimitReached, # check already at this position?
:previousSpeedLimitReached => false,
:speedLimitReached => drivingCourse[end][:v] > CS[:v_limit],
:error => false)
# determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "halt")
while !stateFlags[:endOfCSReached] # s < s_exit
if !stateFlags[:brakingStartReached] # s+s_braking < s_exit
if !stateFlags[:tractionDeficit]
if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0
(CS, drivingCourse, stateFlags) = addBreakFreeSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
elseif stateFlags[:previousSpeedLimitReached]
(CS, drivingCourse, stateFlags) = addClearingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
(CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
# cruise only one step
if settings.stepVariable == :distance
s_cruising = settings.stepSize
elseif settings.stepVariable == time
s_cruising = Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v])
elseif settings.stepVariable == velocity
s_cruising = train.length/(10.0) # TODO which step size should be used?
end
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached]
s_braking = brakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking, settings.approxLevel)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking")
else
stateFlags[:brakingStartReached] = true
end
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached]
s_braking = brakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking, settings.approxLevel)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0 # TODO: define a minimum cruising length?
(CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising")
else
stateFlags[:brakingStartReached] = true
end
else
error()
end
elseif stateFlags[:tractionDeficit]
(CS, drivingCourse, stateFlags) = addDiminishingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
else
error()
# determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "halt")
while !stateFlags[:endOfCSReached] # s < s_exit
if stateFlags[:error]
error("ERROR in calc in CS",csId,": BS=",drivingCourse[end][:behavior]," s=",drivingCourse[end][:s]," s_braking=",s_braking," v_limit=",CS[:v_limit]," v=",drivingCourse[end][:v]," v_exit=",CS[:v_exit]," with the flags: endOfCS: ",stateFlags[:endOfCSReached]," brakingStart: ",stateFlags[:brakingStartReached]," F_T<F_R: ",stateFlags[:tractionDeficit]," F_R<0: ",stateFlags[:resistingForceNegative]," v_previousLimit: ",stateFlags[:previousSpeedLimitReached]," v_limit: ",stateFlags[:speedLimitReached]," error: ",stateFlags[:error])
end
if !stateFlags[:brakingStartReached] # s+s_braking < s_exit
if !stateFlags[:tractionDeficit]
if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0
(drivingCourse, stateFlags) = addBreakFreeSection!(drivingCourse, stateFlags, CSs, csId, settings, train)
elseif stateFlags[:previousSpeedLimitReached]
(drivingCourse, stateFlags) = addClearingSection!(drivingCourse, stateFlags, CSs, csId, settings, train)
elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
(drivingCourse, stateFlags) = addAcceleratingSection!(drivingCourse, stateFlags, CSs, csId, settings, train)
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached]
# cruise only one step
if settings.stepVariable == :distance
s_cruising = settings.stepSize
elseif settings.stepVariable == time
s_cruising = Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v])
elseif settings.stepVariable == velocity
s_cruising = train.length/(10.0) # TODO which step size should be used?
end
(drivingCourse, stateFlags) = addCruisingSection!(drivingCourse, stateFlags, CSs, csId, settings, train, "cruising", s_cruising)
elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached]
s_braking = brakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking, settings.approxLevel)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 0.0
(drivingCourse, stateFlags) = addCruisingSection!(drivingCourse, stateFlags, CSs, csId, settings, train, "downhillBraking", s_cruising)
else
stateFlags[:brakingStartReached] = true
end
elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached]
s_braking = brakingDistance(drivingCourse[end][:v], CS[:v_exit], train.a_braking, settings.approxLevel)
s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking
if s_cruising > 1/10^(settings.approxLevel) # TODO: define another minimum cruising length?
(drivingCourse, stateFlags) = addCruisingSection!(drivingCourse, stateFlags, CSs, csId, settings, train, "cruising", s_cruising)
else
stateFlags[:brakingStartReached] = true
end
else
error()
end
elseif stateFlags[:tractionDeficit]
(drivingCourse, stateFlags) = addDiminishingSection!(drivingCourse, stateFlags, CSs, csId, settings, train)
else
error()
end
else#if !stateFlags[:endOfCSReached] # s < s_exit
(drivingCourse, stateFlags) = addBrakingSection!(drivingCourse, stateFlags, CSs, csId, settings, train)
#else
# error()
end
if CS[:s_exit] - drivingCourse[end][:s] < 1/10^(settings.approxLevel)
drivingCourse[end][:s] = CS[:s_exit] # round s up to CS[:s_exit]
# set state flag
stateFlags[:endOfCSReached] = true
end
else#if !stateFlags[:endOfCSReached] # s < s_exit
(CS, drivingCourse, stateFlags) = addBrakingSection!(CS, drivingCourse, stateFlags, settings, train, CSs)
#else
# error()
end
end
#if s == s_exit
# halt
#end
#if s == s_exit
# halt
#end
# for testing:
# for testing: # TODO
if drivingCourse[end][:s] != CS[:s_exit]
println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit])
end
@ -115,11 +111,9 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t
end
end #for
(CSs[end], drivingCourse) = addHalt!(CSs[end], drivingCourse, settings, train, CSs)
drivingCourse = addHalt!(drivingCourse, CSs, length(CSs), settings, train)
movingSection[:t] = drivingCourse[end][:t] # total running time (in s)
return (movingSection, drivingCourse)
return drivingCourse
end #function calculateMinimumRunningTime
@ -180,7 +174,7 @@ function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, mass
pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train.length * forceFromCoefficient(CSs[csId][:r_path], train.m_train_full)
csId = csId-1
if csId == 0
# TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used
# TODO: currently for values < s_trainrun_start the values of s_trainrun_start will be used
return pathResistance + (CSs[1][:s_entry] - s_rear) / train.length * forceFromCoefficient(CSs[1][:r_path], train.m_train_full)
end #if
end #while
@ -191,30 +185,30 @@ end #function calculatePathResistance
"""
calculate and return tractive and resisting forces for a data point
calculate and return tractive and resisting forces for a support point
"""
function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Train, massModel)
function calculateForces!(supportPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Train, massModel)
# calculate resisting forces
dataPoint[:R_traction] = tractionUnitResistance(dataPoint[:v], train)
supportPoint[:R_traction] = tractionUnitResistance(supportPoint[:v], train)
if train.transportType == :freight
dataPoint[:R_wagons] = freightWagonsResistance(dataPoint[:v], train)
supportPoint[:R_wagons] = freightWagonsResistance(supportPoint[:v], train)
elseif train.transportType == :passenger
dataPoint[:R_wagons] = passengerWagonsResistance(dataPoint[:v], train)
supportPoint[:R_wagons] = passengerWagonsResistance(supportPoint[:v], train)
end
dataPoint[:R_train] = dataPoint[:R_traction] + dataPoint[:R_wagons]
dataPoint[:R_path] = calculatePathResistance(CSs, csId, dataPoint[:s], massModel, train)
dataPoint[:F_R] = dataPoint[:R_train] + dataPoint[:R_path]
supportPoint[:R_train] = supportPoint[:R_traction] + supportPoint[:R_wagons]
supportPoint[:R_path] = calculatePathResistance(CSs, csId, supportPoint[:s], massModel, train)
supportPoint[:F_R] = supportPoint[:R_train] + supportPoint[:R_path]
# calculate tractive effort
if bsType == "braking" || bsType == "coasting" || bsType == "halt"
dataPoint[:F_T] = 0.0
supportPoint[:F_T] = 0.0
elseif bsType == "cruising"
dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train.tractiveEffort))
supportPoint[:F_T] = min(max(0.0, supportPoint[:F_R]), calculateTractiveEffort(supportPoint[:v], train.tractiveEffort))
else # bsType == "accelerating" || bsType == "diminishing" || 'default'
dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort)
supportPoint[:F_T] = calculateTractiveEffort(supportPoint[:v], train.tractiveEffort)
end
return dataPoint
return supportPoint
end #function calculateForces!
@ -226,55 +220,54 @@ function moveAStep(previousPoint::Dict, stepVariable::Symbol, stepSize::Real, cs
# TODO: csId is only for error messages. Should it be removed?
#= 08/31 TODO: How to check if the train stopps during this step? I should throw an error myself that I catch in higher hierarchies. =#
# create the next data point
newPoint = DataPoint()
newPoint[:i] = previousPoint[:i]+1 # identifier
# create the next support point
newPoint = SupportPoint()
# calculate s, t, v, E
if stepVariable == :distance # distance step method
newPoint[:Δs] = stepSize # step size (in m)
Δs = stepSize # step size (in m)
if previousPoint[:a] == 0.0
if previousPoint[:v] == 0.0
error("ERROR: The train tries to cruise at v=0.0 m/s at s=",previousPoint[:s]," in CS",csId,".")
end
newPoint[:Δt] = Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s)
newPoint[:Δv] = 0.0 # step size (in m/s)
Δt = Δt_with_constant_v(Δs, previousPoint[:v]) # step size (in s)
Δv = 0.0 # step size (in m/s)
else
# check if the parts of the following square roots will be <0.0 in the functions Δt_with_Δs and Δv_with_Δs
squareRootPartIsNegative = (previousPoint[:v]/previousPoint[:a])^2+2*newPoint[:Δs]/previousPoint[:a] < 0.0 || previousPoint[:v]^2+2*newPoint[:Δs]*previousPoint[:a] < 0.0
squareRootPartIsNegative = (previousPoint[:v]/previousPoint[:a])^2+2*Δs/previousPoint[:a] < 0.0 || previousPoint[:v]^2+2*Δs*previousPoint[:a] < 0.0
if previousPoint[:a] < 0.0 && squareRootPartIsNegative
error("ERROR: The train stops during the accelerating section in CS",csId," because the tractive effort is lower than the resistant forces.",
" Before the stop the last point has the values s=",previousPoint[:s]," m, v=",previousPoint[:v]," m/s, a=",previousPoint[:a]," m/s^2,",
" F_T=",previousPoint[:F_T]," N, R_traction=",previousPoint[:R_traction]," N, R_wagons=",previousPoint[:R_wagons]," N, R_path=",previousPoint[:R_path]," N.")
end
newPoint[:Δt] = Δt_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in s)
newPoint[:Δv] = Δv_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in m/s)
Δt = Δt_with_Δs(Δs, previousPoint[:a], previousPoint[:v]) # step size (in s)
Δv = Δv_with_Δs(Δs, previousPoint[:a], previousPoint[:v]) # step size (in m/s)
end
elseif stepVariable == :time # time step method
newPoint[:Δt] = stepSize # step size (in s)
newPoint[:Δs] = Δs_with_Δt(newPoint[:Δt], previousPoint[:a], previousPoint[:v]) # step size (in m)
newPoint[:Δv] = Δv_with_Δt(newPoint[:Δt], previousPoint[:a]) # step size (in m/s)
Δt = stepSize # step size (in s)
Δs = Δs_with_Δt(Δt, previousPoint[:a], previousPoint[:v]) # step size (in m)
Δv = Δv_with_Δt(Δt, previousPoint[:a]) # step size (in m/s)
elseif stepVariable == :velocity # velocity step method
if previousPoint[:a] == 0.0
if previousPoint[:v] == 0.0
error("ERROR: The train tries to cruise at v=0.0 m/s at s=",previousPoint[:s]," in CS",csId,".")
end
newPoint[:Δs] = stepSize # step size (in m)
Δs = stepSize # step size (in m)
# TODO what is the best default step size for constant v? define Δs or Δt?
newPoint[:Δt] = Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s)
newPoint[:Δv] = 0.0 # step size (in m/s)
Δt = Δt_with_constant_v(Δs, previousPoint[:v]) # step size (in s)
Δv = 0.0 # step size (in m/s)
else
newPoint[:Δv] = stepSize * sign(previousPoint[:a]) # step size (in m/s)
newPoint[:Δs] = Δs_with_Δv(newPoint[:Δv], previousPoint[:a], previousPoint[:v]) # step size (in m)
newPoint[:Δt] = Δt_with_Δv(newPoint[:Δv], previousPoint[:a]) # step size (in s)
Δv = stepSize * sign(previousPoint[:a]) # step size (in m/s)
Δs = Δs_with_Δv(Δv, previousPoint[:a], previousPoint[:v]) # step size (in m)
Δt = Δt_with_Δv(Δv, previousPoint[:a]) # step size (in s)
end
end #if
newPoint[:s] = previousPoint[:s] + newPoint[:Δs] # position (in m)
newPoint[:t] = previousPoint[:t] + newPoint[:Δt] # point in time (in s)
newPoint[:v] = previousPoint[:v] + newPoint[:Δv] # velocity (in m/s)
newPoint[:s] = previousPoint[:s] + Δs # position (in m)
newPoint[:t] = previousPoint[:t] + Δt # point in time (in s)
newPoint[:v] = previousPoint[:v] + Δv # velocity (in m/s)
return newPoint
end #function moveAStep
@ -283,30 +276,30 @@ end #function moveAStep
"""
# if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept
"""
function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::Real, trainLength::Real)
function getLowestSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::Real, trainLength::Real)
v_limit = CSs[csWithTrainHeadId][:v_limit]
s_exit = CSs[csWithTrainHeadId][:s_exit]
if csWithTrainHeadId > 1 && s -trainLength < CSs[csWithTrainHeadId][:s_entry]
formerCsId = csWithTrainHeadId-1
while formerCsId > 0 && s -trainLength < CSs[formerCsId][:s_exit]
if CSs[formerCsId][:v_limit] < v_limit # TODO: is the position of the train's rear < movingSection[:s_entry], v_limit of the first CS is used
if CSs[formerCsId][:v_limit] < v_limit # TODO: is the position of the train's rear < s_trainrun_start, v_limit of the first CS is used
v_limit = CSs[formerCsId][:v_limit]
s_exit = CSs[formerCsId][:s_exit]
end
formerCsId = formerCsId -1
end
end
currentSpeedLimit = Dict(:v => v_limit, :s_end => s_exit + trainLength)
return currentSpeedLimit
end #function getCurrentSpeedLimit
lowestSpeedLimit = Dict(:v => v_limit, :s_end => s_exit + trainLength)
return lowestSpeedLimit
end #function getLowestSpeedLimit
"""
TODO
"""
function getNextPointOfInterest(pointsOfInterest::Vector{Tuple}, s::Real)
function getNextPointOfInterest(pointsOfInterest::Vector{NamedTuple}, s::Real)
for POI in pointsOfInterest
if POI[1] > s
if POI[:s] > s
return POI
end
end
@ -314,12 +307,23 @@ function getNextPointOfInterest(pointsOfInterest::Vector{Tuple}, s::Real)
end #function getNextPointOfInterest
## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior
## create vectors with the moving section's points of interest and with the characteristic sections with secured braking and accelerating behavior
function determineCharacteristics(path::Path, train::Train, settings::Settings)
movingSection = MovingSection(path, train.v_limit, train.length)
movingSection = secureBrakingBehavior!(movingSection, train.a_braking, settings.approxLevel)
movingSection = secureAcceleratingBehavior!(movingSection, settings, train)
#movingSection = secureCruisingBehavior!(movingSection, settings, train)
# determine the positions of the points of interest depending on the interesting part of the train (front/rear) and the train's length
pointsOfInterest = NamedTuple[]
if !isempty(path.poi)
for POI in path.poi
s_poi = POI[:station]
if POI[:measure] == "rear"
s_poi += train.length
end
push!(pointsOfInterest, (s = s_poi, label = POI[:label]) )
end
sort!(pointsOfInterest, by = x -> x[:s])
end
return movingSection
characteristicSections = CharacteristicSections(path, train.v_limit, train.length, pointsOfInterest)
characteristicSections = secureBrakingBehavior!(characteristicSections, train.a_braking, settings.approxLevel)
return (characteristicSections, pointsOfInterest)
end #function determineCharacteristics

View File

@ -51,7 +51,7 @@ function Settings(
"outputDetail": {
"description": "Selecting the detail of the result",
"type": "string",
"enum": [ "running_time", "points_of_interest", "driving_course" ]
"enum": [ "running_time", "points_of_interest", "data_points", "driving_course" ]
},
"outputFormat": {
"description": "Output format",
@ -254,9 +254,9 @@ function Path(file, type = :YAML)
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 ‰)
station = elem[1] # station in m
label = elem[2] # name
measure = elem[3] # front or rear
point = Dict(:station => station,
:label => label,
@ -613,128 +613,68 @@ function Train(file, type = :YAML)
end #function Train() # outer constructor
## create a moving section containing characteristic sections
function MovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real)
# this function creates and returns a moving section dependent on the paths attributes
## create the moving section's characteristic sections
function CharacteristicSections(path::Path, v_trainLimit::Real, s_trainLength::Real, MS_poi::Vector{NamedTuple})
# create and return the characteristic sections of 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)
##TODO: use a tuple with naming
pointsOfInterest = Tuple[]
if !isempty(path.poi)
for POI in path.poi
s_poi = POI[:station]
if POI[:measure] == "rear"
s_poi += s_trainLength
end
push!(pointsOfInterest, (s_poi, POI[:label]) )
end
sort!(pointsOfInterest, by = x -> x[1])
end
CSs=Vector{Dict}()
s_csStart=s_entry
csId=1
CSs = Vector{Dict}()
s_csStart = path.sections[1][:s_start] # first position (in m)
#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
push!(CSs, CharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, pointsOfInterest))
push!(CSs, CharacteristicSection(s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, MS_poi))
s_csStart = currentSection[:s_start]
csId = csId+1
#csId = csId+1
end #if
end #for
push!(CSs, CharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, pointsOfInterest))
push!(CSs, CharacteristicSection(s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, MS_poi))
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)
:characteristicSections => CSs, # list of containing characteristic sections
:pointsOfInterest => pointsOfInterest) # list of containing points of interest
return CSs
end #function CharacteristicSections
return movingSection
end #function MovingSection
## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections.
function CharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, MS_poi::Vector{Tuple})
## create a characteristic section for a path section.
function CharacteristicSection(s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, MS_poi::Vector{NamedTuple})
# 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)
: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)
characteristicSection::Dict{Symbol, Any} = Dict(:s_entry => s_entry, # first position (in m)
:s_exit => section[:s_end], # last position (in m)
:r_path => section[:f_Rp], # path resistance (in ‰)
:v_limit => v_limit, # speed limit (in m/s)
:v_exit => v_limit) # maximum exit speed (in m/s) initialized with v_limit
# list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated
# get the list of positions of every point of interest (POI) in this charateristic section for which support points should be calculated from the list of the whole moving section's POI
s_exit = characteristicSection[:s_exit]
##TODO: use a tuple with naming
pointsOfInterest = Tuple[]
CS_poi = NamedTuple[]
if !isempty(MS_poi)
for POI in MS_poi
s_poi = POI[1]
s_poi = POI[:s]
if s_entry < s_poi && s_poi <= s_exit
push!(pointsOfInterest, (POI))
push!(CS_poi, POI)
end
end
end
if isempty(pointsOfInterest) || pointsOfInterest[end][1] < s_exit
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
if isempty(CS_poi) || CS_poi[end][:s] < s_exit
push!(CS_poi, (s = s_exit, label = "")) # s_exit has to be the last POI so that there will always be a POI to campare the current position with
end
merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest))
merge!(characteristicSection, Dict(:pointsOfInterest => CS_poi))
return characteristicSection
end #function CharacteristicSection
"""
BehaviorSection() TODO!
a SupportPoint is the smallest element of the driving course. One step of the step approach is between two support points
"""
function BehaviorSection(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 BehaviorSection
"""
a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points
"""
function DataPoint()
dataPoint = Dict(
:i => 0, # identifier and counter variable of the driving course
:behavior => "", # type of behavior section the data point is part of - see BehaviorSection()
# 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
function SupportPoint()
supportPoint = Dict(
:behavior => "", # type of behavior section the support point is part of - see BehaviorSection()
# a support 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)
@ -743,5 +683,5 @@ function DataPoint()
:R_wagons => 0.0, # set of wagons resistance (in N)
:label => "" # a label for important points
)
return dataPoint
end #function DataPoint
return supportPoint
end #function SupportPoint

View File

@ -124,8 +124,8 @@ function Δs_with_Δt(Δt::Real, a_prev::Real, v_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δt: time step (in s)
# a_prev: acceleration from previous data point
# v_prev: velocitiy from previous data point
# a_prev: acceleration from previous support point
# v_prev: velocitiy from previous support point
Δs = Δt * (2*v_prev + Δt*a_prev) /2 # step size (in m)
return Δs
end #function Δs_with_Δt
@ -134,8 +134,8 @@ function Δs_with_Δv(Δv::Real, a_prev::Real, v_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δv: velocity step (in m/s)
# a_prev: acceleration from previous data point
# v_prev: velocitiy from previous data point
# a_prev: acceleration from previous support point
# v_prev: velocitiy from previous support point
Δs = ((v_prev + Δv)^2 - v_prev^2)/2/a_prev # step size (in m)
return Δs
end #function Δs_with_Δv
@ -144,8 +144,8 @@ function Δt_with_Δs(Δs::Real, a_prev::Real, v_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δs: distance step (in m)
# a_prev: acceleration from previous data point
# v_prev: velocitiy from previous data point
# a_prev: acceleration from previous support point
# v_prev: velocitiy from previous support point
Δt = sign(a_prev) *sqrt((v_prev /a_prev)^2 + 2 *Δs /a_prev) - v_prev /a_prev # step size (in m/s)
return Δt
@ -155,7 +155,7 @@ function Δt_with_Δv(Δv::Real, a_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δv: velocity step (in m/s)
# a_prev: acceleration from previous data point
# a_prev: acceleration from previous support point
Δt = Δv /a_prev # step size (in s)
return Δt
end #function Δt_with_Δv
@ -173,8 +173,8 @@ function Δv_with_Δs(Δs::Real, a_prev::Real, v_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δs: distance step (in m)
# a_prev: acceleration from previous data point
# v_prev: velocitiy from previous data point
# a_prev: acceleration from previous support point
# v_prev: velocitiy from previous support point
Δv = sqrt(v_prev^2 + 2*Δs*a_prev) - v_prev # step size (in m/s)
return Δv
end #function Δv_with_Δs
@ -183,7 +183,7 @@ function Δv_with_Δt(Δt::Real, a_prev::Real)
# equation is based on [Wende:2003, page 37]
# Δt: time step (in s)
# a_prev: acceleration from previous data point
# a_prev: acceleration from previous support point
Δv = Δt * a_prev # step size (in m/s)
return Δv
end #function Δv_with_Δt

View File

@ -4,41 +4,59 @@
# __copyright__ = "2020-2022"
# __license__ = "ISC"
function createOutput(settings::Settings, drivingCourse::Vector{Dict}, pointsOfInterest::Vector{Tuple})
function createOutput(settings::Settings, drivingCourse::Vector{Dict}, pointsOfInterest::Vector{NamedTuple})
if settings.outputDetail == :running_time
output::Vector{Dict} = [Dict(:t => drivingCourse[end][:t])]
elseif settings.outputDetail == :points_of_interest && !isempty(pointsOfInterest)
# get only the driving course's data points with POI labels
elseif settings.outputDetail == :points_of_interest
# get only the driving course's support points with POI labels
# if there is no point with POI label return the information of departure and arrival (first and last points)
output = Dict[]
dataPoint = 1
for POI in 1:length(pointsOfInterest)
while dataPoint <= length(drivingCourse)
if pointsOfInterest[POI][1] == drivingCourse[dataPoint][:s]
push!(output, drivingCourse[dataPoint])
break
if isempty(pointsOfInterest)
push!(output, drivingCourse[1])
push!(output, drivingCourse[end])
else
supportPoint = 1
for POI in 1:length(pointsOfInterest)
while supportPoint <= length(drivingCourse)
if pointsOfInterest[POI][:s] == drivingCourse[supportPoint][:s]
push!(output, drivingCourse[supportPoint])
break
end
supportPoint += 1
end
dataPoint += 1
end
end
else #if settings.outputDetail == :driving_course || (settings.outputDetail == :points_of_interest && !isempty(path.poi))
elseif settings.outputDetail == :data_points
# get the driving course's support points where a new behavior section starts and the driving mode changes
output = Dict[]
# the first support point is the first data point
push!(output, drivingCourse[1])
for supportPoint in 2:length(drivingCourse)
if drivingCourse[supportPoint-1][:behavior] != drivingCourse[supportPoint][:behavior]
push!(output, drivingCourse[supportPoint])
end
end
elseif settings.outputDetail == :driving_course
output = drivingCourse
end
if settings.outputFormat == :dataframe
return createDataFrame(output, settings.outputDetail)
return createDataFrame(output, settings.outputDetail, settings.approxLevel)
elseif settings.outputFormat == :vector
return output
end
end
function createDataFrame(output_vector::Vector{Dict}, outputDetail)
function createDataFrame(output_vector::Vector{Dict}, outputDetail, approxLevel::Int)
if outputDetail == :running_time
# create a DataFrame with running time information
dataFrame = DataFrame(t=[output_vector[end][:t]])
else # :points_of_interest or :driving_course
dataFrame = DataFrame(t=[round(output_vector[end][:t], digits=approxLevel)])
else # :points_of_interest, :data_points or :driving_course
columnSymbols = [:label, :behavior, :s, :v, :t, :a, :F_T, :F_R, :R_path, :R_traction, :R_wagons]
allColumns = []
@ -54,6 +72,7 @@ function createDataFrame(output_vector::Vector{Dict}, outputDetail)
for point in output_vector
push!(currentRealColumn, point[columnSymbols[column]])
end
currentRealColumn = round.(currentRealColumn, digits=approxLevel)
push!(allColumns, currentRealColumn)
end
end # for
@ -63,4 +82,4 @@ function createDataFrame(output_vector::Vector{Dict}, outputDetail)
end
return dataFrame
end #createDataFrameForDrivingCourse
end #createDataFrame

View File

@ -10,7 +10,7 @@ struct Settings
stepVariable::Symbol # variable of the linear multistep method: ":distance", ":time" or ":velocity".
stepSize::Real # step size, unit depends on stepVariable - :distance in meter, time in seconds and velocity in meter/second.
approxLevel::Int # value for approximation; used when rounding or iterating.
outputDetail::Symbol # single Float() ":running_time", Vector() of ":points_of_interest",
outputDetail::Symbol # single Float() ":running_time", Vector() of ":points_of_interest", Vector() of ":data_points"
# or complete Vector() ":driving_course"
outputFormat::Symbol # output as ":dataframe" or as ":vector".

View File

@ -1,4 +1,4 @@
%YAML 1.2
---
settings:
outputDetail: "driving_course" # single value "running_time", list of "points_of_interest", complete "driving_course"
outputDetail: "driving_course" # single value "running_time", list of "points_of_interest", list of "data_points", complete "driving_course"

View File

@ -1,4 +1,4 @@
%YAML 1.2
---
settings:
outputDetail: "points_of_interest" # single value "running_time", list of "points_of_interest", complete "driving_course"
outputDetail: "points_of_interest" # single value "running_time", list of "points_of_interest", list of "data_points", complete "driving_course"