diff --git a/src/Behavior.jl b/src/Behavior.jl deleted file mode 100644 index d1815d2..0000000 --- a/src/Behavior.jl +++ /dev/null @@ -1,1398 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -## functions for calculating tractive effort and resisting forces -""" - calculateTractiveEffort(v, tractiveEffortVelocityPairs) - -Calculate the trains tractive effort with the `tractiveEffortVelocityPairs` dependend on the velocity `v`. - -... -# Arguments -- `v::AbstractFloat`: the current velocity in m/s. -- `tractiveEffortVelocityPairs::Array{Array{AbstractFloat,1},1}`: the trains pairs for velocity in m/s and tractive effort in N as one array containing an array for each pair. -... - -# Examples -```julia-repl -julia> calculateTractiveEffort(20.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) -100000 - -julia> calculateTractiveEffort(30.0, [[0.0, 180000], [20.0, 100000], [40.0, 60000], [60.0, 40000], [80.0, 30000]]) -80000 -``` -""" -function calculateTractiveEffort(v::AbstractFloat, tractiveEffortVelocityPairs) - for row in 1:length(tractiveEffortVelocityPairs) - nextPair = tractiveEffortVelocityPairs[row] - if nextPair[1] == v - return nextPair[2] - elseif nextPair[1] > v - # interpolate for a straight line between the two surrounding points with the formula: F=(v-v_(row-1))*(F_row-F_(row-1))/(v_row-v_(row-1))+F_(row-1) - previousPair = tractiveEffortVelocityPairs[row-1] - F_T_interpolation = (v-previousPair[1]) * (nextPair[2]-previousPair[2]) / (nextPair[1]-previousPair[1]) + previousPair[2] - return F_T_interpolation - end #if - end #for - # if v gets higher than the velocities in tractiveEffortVelocityPairs the last tractive effort will be used - # TODO: also an extrapolation could be used - return tractiveEffortVelocityPairs[end][2] -end #function calculateTractiveEffort - -""" -calculate and return the path resistance dependend on the trains position and mass model -""" -function calculatePathResistance(CSs::Vector{Dict}, csId::Integer, s::Real, massModel, train::Dict) - - if massModel == :mass_point - pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) - elseif massModel == :homogeneous_strip - pathResistance = 0.0 - s_rear = s - train[:length] # position of the rear of the train - while csId > 0 && s_rear < CSs[csId][:s_exit] - pathResistance = pathResistance + (min(s, CSs[csId][:s_exit]) - max(s_rear, CSs[csId][:s_entry])) / train[:length] * calcForceFromCoefficient(CSs[csId][:r_path], train[:m_train]) - csId = csId-1 - if csId == 0 - # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used - return pathResistance + (CSs[1][:s_entry] - s_rear) / train[:length] * calcForceFromCoefficient(CSs[1][:r_path], train[:m_train]) - end #if - end #while - end #if - - return pathResistance -end #function calculatePathResistance - -""" -calculate and return tractive and resisting forces for a data point -""" -function calculateForces!(dataPoint::Dict, CSs::Vector{Dict}, csId::Integer, bsType::String, train::Dict, massModel) - # calculate resisting forces - dataPoint[:R_traction] = calcTractionUnitResistance(dataPoint[:v], train) - dataPoint[:R_wagons] = calcWagonsResistance(dataPoint[:v], train) - dataPoint[:R_train] = dataPoint[:R_traction] + dataPoint[:R_wagons] - dataPoint[:R_path] = calculatePathResistance(CSs, csId, dataPoint[:s], massModel, train) - dataPoint[:F_R] = dataPoint[:R_train] + dataPoint[:R_path] - - # calculate tractive effort - if bsType == "braking" || bsType == "coasting" - dataPoint[:F_T] = 0.0 - elseif bsType == "cruising" - dataPoint[:F_T] = min(max(0.0, dataPoint[:F_R]), calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs])) - else # bsType == "accelerating" || bsType == "diminishing" || 'default' - dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train[:tractiveEffortVelocityPairs]) - end - - return dataPoint -end #function calculateForces! - - -""" -TODO -""" -function moveAStep(previousPoint::Dict, stepVariable::Symbol, stepSize::Real, csId::Integer) - # stepSize is the currentStepSize depending on the accessing function - # TODO: csId is only for error messages. Should it be removed? - #= 08/31 TODO: How to check if the train stopps during this step? I should throw an error myself that I catch in higher hierarchies. =# - - # create the next data point - newPoint = createDataPoint() - newPoint[:i] = previousPoint[:i]+1 # identifier - - # calculate s, t, v, E - if stepVariable == :distance # distance step method - newPoint[:Δs] = stepSize # step size (in m) - if previousPoint[:a] == 0.0 - if previousPoint[:v] == 0.0 - error("ERROR: The train tries to cruise at v=0.0 m/s at s=",previousPoint[:s]," in CS",csId,".") - end - newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = 0.0 # step size (in m/s) - else - # check if the parts of the following square roots will be <0.0 in the functions calc_Δt_with_Δs and calc_Δv_with_Δs - squareRootPartIsNegative = (previousPoint[:v]/previousPoint[:a])^2+2*newPoint[:Δs]/previousPoint[:a] < 0.0 || previousPoint[:v]^2+2*newPoint[:Δs]*previousPoint[:a] < 0.0 - if previousPoint[:a] < 0.0 && squareRootPartIsNegative - error("ERROR: The train stops during the accelerating section in CS",csId," because the tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",previousPoint[:s]," m, v=",previousPoint[:v]," m/s, a=",previousPoint[:a]," m/s^2,", - " F_T=",previousPoint[:F_T]," N, R_traction=",previousPoint[:R_traction]," N, R_wagons=",previousPoint[:R_wagons]," N, R_path=",previousPoint[:R_path]," N.") - end - newPoint[:Δt] = calc_Δt_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = calc_Δv_with_Δs(newPoint[:Δs], previousPoint[:a], previousPoint[:v]) # step size (in m/s) - end - - elseif stepVariable == :time # time step method - newPoint[:Δt] = stepSize # step size (in s) - newPoint[:Δs] = calc_Δs_with_Δt(newPoint[:Δt], previousPoint[:a], previousPoint[:v]) # step size (in m) - newPoint[:Δv] = calc_Δv_with_Δt(newPoint[:Δt], previousPoint[:a]) # step size (in m/s) - - elseif stepVariable == :velocity # velocity step method - if previousPoint[:a] == 0.0 - if previousPoint[:v] == 0.0 - error("ERROR: The train tries to cruise at v=0.0 m/s at s=",previousPoint[:s]," in CS",csId,".") - end - newPoint[:Δs] = stepSize # step size (in m) - # TODO what is the best default step size for constant v? define Δs or Δt? - newPoint[:Δt] = calc_Δt_with_constant_v(newPoint[:Δs], previousPoint[:v]) # step size (in s) - newPoint[:Δv] = 0.0 # step size (in m/s) - else - newPoint[:Δv] = stepSize * sign(previousPoint[:a]) # step size (in m/s) - newPoint[:Δs] = calc_Δs_with_Δv(newPoint[:Δv], previousPoint[:a], previousPoint[:v]) # step size (in m) - newPoint[:Δt] = calc_Δt_with_Δv(newPoint[:Δv], previousPoint[:a]) # step size (in s) - end - end #if - - newPoint[:s] = previousPoint[:s] + newPoint[:Δs] # position (in m) - newPoint[:t] = previousPoint[:t] + newPoint[:Δt] # point in time (in s) - newPoint[:v] = previousPoint[:v] + newPoint[:Δv] # velocity (in m/s) - newPoint[:ΔW] = calc_ΔW(previousPoint[:F_T], newPoint[:Δs]) # mechanical work in this step (in Ws) - newPoint[:W] = previousPoint[:W] + newPoint[:ΔW] # mechanical work (in Ws) - newPoint[:ΔE] = calc_ΔE(newPoint[:ΔW]) # energy consumption in this step (in Ws) - newPoint[:E] = previousPoint[:E] + newPoint[:ΔE] # energy consumption (in Ws) - - - return newPoint -end #function moveAStep - -""" -# if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept -""" -function getCurrentSpeedLimit(CSs::Vector{Dict}, csWithTrainHeadId::Integer, s::Real, trainLength::Real) - v_limit = CSs[csWithTrainHeadId][:v_limit] - s_exit = CSs[csWithTrainHeadId][:s_exit] - if csWithTrainHeadId > 1 && s -trainLength < CSs[csWithTrainHeadId][:s_entry] - formerCsId = csWithTrainHeadId-1 - while formerCsId > 0 && s -trainLength < CSs[formerCsId][:s_exit] - if CSs[formerCsId][:v_limit] < v_limit # TODO: is the position of the train's rear < movingSection[:s_entry], v_limit of the first CS is used - v_limit = CSs[formerCsId][:v_limit] - s_exit = CSs[formerCsId][:s_exit] - end - formerCsId = formerCsId -1 - end - end - currentSpeedLimit = Dict(:v => v_limit, :s_end => s_exit + trainLength) - return currentSpeedLimit -end #function getCurrentSpeedLimit - -function getNextPointOfInterest(pointsOfInterest::Vector{Real}, s::Real) - for s_POI in pointsOfInterest - if s_POI > s - return s_POI - end - end - error("ERROR in getNextPointOfInterest: There is no POI higher than s=",s," m.") -end #function getNextPointOfInterest - -## This function calculates the data points of the breakFree section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for breakFree if needed. -# Info: currently the values of the breakFree section will be calculated like in the accelerating section -function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # conditions for the break free section - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - trainIsHalting = drivingCourse[end][:v] == 0.0 - - if trainIsHalting && !endOfCSReached - BS = createBehaviorSection("breakFree", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) # currently the tractive effort is calculated like in the accelerating section - - # calculate the breakFree section with calculating the accelerating section and just using the first step and removing the rest - try (CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - catch(acceleratingError) - println("This error happened during the break free phase that is using the accelerating function:") - rethrow(acceleratingError) - end - - # delete every dataPoint except the first two - while drivingCourse[end][:i] > drivingCourse[BS[:dataPoints][1]][:i] +1 - pop!(drivingCourse) - end - - # change the accelerating data to break free - drivingCourse[end-1][:behavior] = BS[:type] - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - # remove the accelerating section from the CS - CS[:t] = CS[:t] - get(CS[:behaviorSections], :accelerating, Dict(:t=>0.0))[:t] # total running time (in s) - CS[:E] = CS[:E] - get(CS[:behaviorSections], :accelerating, Dict(:E=>0.0))[:E] # total energy consumption (in Ws) - delete!(CS[:behaviorSections], :accelerating) - - # calculate the accumulated breakFree section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:breakFree => BS)) - end # else: return the characteristic section without a breakFree section - - # determine state flags - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - s_braking = 0.0 - else - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # reset state flags - stateFlags[:endOfCSReached] = drivingCourse[end][:s] >= CS[:s_exit] - stateFlags[:brakingStartReached] = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] # or add another flag for equal forces? - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:previousSpeedLimitReached] = false - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_limit] - stateFlags[:error] = !(stateFlags[:endOfCSReached] || stateFlags[:brakingStartReached] || stateFlags[:tractionDeficit] || stateFlags[:previousSpeedLimitReached] || stateFlags[:speedLimitReached]) - - return (CS, drivingCourse, stateFlags) -end #function addBreakFreeSection! - -## This function calculates the data points of the clearing section. - # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the clearing section. -function addClearingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - if stateFlags[:previousSpeedLimitReached] - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - s_clearing = min(CS[:s_exit]-drivingCourse[end][:s]-s_braking, currentSpeedLimit[:s_end] - drivingCourse[end][:s]) - if s_clearing > 0.0 - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_clearing, settings, train, CSs, "clearing") - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) - # stateFlags[:brakingStartReached] = brakingStartReached - # stateFlags[:endOfCSReached] = stateFlags[:endOfCSReached] || drivingCourse[end][:s] == CS[:s_exit] - else - error("ERROR: clearing <=0.0 although it has to be >0.0 in CS ",CS[:id]) - end - #stateFlags[:previousSpeedLimitReached] = false - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - else - stateFlags[:error] = true - end - - return (CS, drivingCourse, stateFlags) -end #function addClearingSection - -## This function calculates the data points of the accelerating section. - # Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the accelerating section -function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - #function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}, ignoreBraking::Bool) - #=if drivingCourse would also be part of movingSectiong: function addAcceleratingSection!(movingSection::Dict, stateFlags::Dict, csId::Integer, settings::Settings, train::Dict) - CSs = movingSection[:characteristicSections] - CS = CSs[csId] - drivingCourse = movingSection[:drivingCourse]=# - - calculateForces!(drivingCourse[end], CSs, CS[:id], "accelerating", train, settings.massModel) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for the accelerating section - targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] || stateFlags[:speedLimitReached] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - previousSpeedLimitReached = stateFlags[:previousSpeedLimitReached] - - # use the conditions for the accelerating section - if !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached - BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - previousSpeedLimitReached = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - speedLimitReached = drivingCourse[end][:v] >= CS[:v_limit] - #speedLimitReached = drivingCourse[end][:v] > currentSpeedLimit[:v] - #targetSpeedReached = speedLimitReached - while !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached && !previousSpeedLimitReached - currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - while !targetSpeedReached && !speedLimitReached && !brakingStartReached && !pointOfInterestReached && tractionSurplus && !previousSpeedLimitReached - # 03/08 old: while drivingCourse[end][:v] < CS[:v_peak] && drivingCourse[end][:v] <= currentSpeedLimit[:v] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T] > drivingCourse[end][:F_R] # as long as s_i + s_braking < s_CSexit - if drivingCourse[end][:s] >= currentSpeedLimit[:s_end] - # could be asked after creating an data point. This way here prevents even a minimal exceedance of speed limit will be noticed. On the other hand the train cruises possibly a little to long - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - end - - # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) - - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # conditions for the next while cycle - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - speedLimitReached = drivingCourse[end][:v] > CS[:v_limit] - previousSpeedLimitReached = currentSpeedLimit[:v] < CS[:v_limit] && (drivingCourse[end][:v] > currentSpeedLimit[:v] || (drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end])) - targetSpeedReached = drivingCourse[end][:v] >= CS[:v_peak] - #targetSpeedReached = speedLimitReached - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well - tractionSurplus = drivingCourse[end][:F_T] > drivingCourse[end][:F_R] - end #while - - if CS[:id]==0 - testFlag = true - else - testFlag = false # for testing - end - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:F_T] <= drivingCourse[end][:F_R] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," <= F_R=",drivingCourse[end][:F_R]) # for testing - currentStepSize = settings.stepSize / 10.0^cycle - - elseif s_braking > 0.0 && drivingCourse[end][:s] + s_braking > CS[:s_exit] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings.stepSize / 10.0^cycle - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:v] > CS[:v_peak] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=",CS[:v_peak]) # for testing - if settings.stepVariable == :speed - currentStepSize = CS[:v_peak]-drivingCourse[end-1][:v] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:v] > currentSpeedLimit[:v] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_limitCurrent=",currentSpeedLimit[:v]) # for testing - if settings.stepVariable == :velocity - currentStepSize = currentSpeedLimit[:v]-drivingCourse[end-1][:v] - - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing - if s_braking == 0.0 - endOfCSReached = true - end - break - - elseif drivingCourse[end][:v] == CS[:v_peak] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_peak=",CS[:v_peak]) # for testing - break - - elseif drivingCourse[end][:v] == currentSpeedLimit[:v] && drivingCourse[end][:s] < currentSpeedLimit[:s_end] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing - if nextPointOfInterest == CS[:s_exit] - endOfCSReached = true - end - break - - else - println("v=",drivingCourse[end][:v]," v_peak= ", CS[:v_peak] , " v_cLimit=", currentSpeedLimit[:v]) - println("s=" ,drivingCourse[end][:s]," s_exit=", CS[:s_exit], " s+s_braking=", drivingCourse[end][:s] +s_braking," nextPOI=",nextPointOfInterest) - println("F_T=",drivingCourse[end][:F_T] ," F_R=", drivingCourse[end][:F_R]) - - error("ERROR at accelerating section: With the step variable ",settings.stepVariable," the while loop will be left although v CS[:v_peak] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=",CS[:v_peak]) # for testing - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next section - brakingStartReached = false - - elseif drivingCourse[end][:s] + s_braking > CS[:s_exit] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - if s_braking > 0.0 - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - else - drivingCourse[end][:s] = CS[:s_exit] # round s down to CS[:s_exit] - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - end - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - - elseif drivingCourse[end][:F_T] <= drivingCourse[end][:F_R] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," <= F_R=",drivingCourse[end][:F_R]) # for testing - - elseif drivingCourse[end][:v] > currentSpeedLimit[:v] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," > v_limitCurrent=",currentSpeedLimit[:v]) # for testing - previousSpeedLimitReached = true - - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - else - if drivingCourse[end][:s] + s_braking == CS[:s_exit] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," else case and there: s +s_braking=", drivingCourse[end][:s],",+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - elseif drivingCourse[end][:v] == currentSpeedLimit[:v] - testFlag && println("in CS",CS[:id]," accelerating cycle",cycle," case: v=", drivingCourse[end][:v]," == v_limitCurrent=",currentSpeedLimit[:v]) # for testing - - end - end - - # TODO is it possible to put this into to the if-fork? - if drivingCourse[end][:s] == CS[:s_exit] - endOfCSReached = true - end - - end - end #for - - if drivingCourse[end][:s] == CS[:s_exit] - endOfCSReached = true - end - - end #while - - if length(BS[:dataPoints]) > 1 - # calculate the accumulated accelerating section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - # 03/10 old: CS[:v_peak] = max(drivingCourse[end][:v], CS[:v_entry]) # setting v_peak to the last data points velocity which is the highest reachable value in this characteristic section or to v_entry in case it is higher when running on a path with high resistances - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = !(tractionSurplus || drivingCourse[end][:F_T] == drivingCourse[end][:F_R]) # or add another flag for equal forces? - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:previousSpeedLimitReached] = previousSpeedLimitReached - stateFlags[:speedLimitReached] = targetSpeedReached - stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) - - return (CS, drivingCourse, stateFlags) -end #function addAcceleratingSection! - - -## This function calculates the data points of the cruising section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for cruising if needed. -function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, s_cruising::Real, settings::Settings, train::Dict, CSs::Vector{Dict}, cruisingType::String) - trainIsClearing = cruisingType == "clearing" - trainIsBrakingDownhill = cruisingType == "downhillBraking" - - # traction effort and resisting forces (in N) - if !trainIsBrakingDownhill # TODO: or just give BS[:type] instead of "cruising"/"braking"? - calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - else - calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - end - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for cruising section - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - speedIsValid = drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - targetPositionReached = s_cruising == 0.0 - resistingForceNegative = drivingCourse[end][:F_R] < 0 -#println(" vor if speedIsValid=",speedIsValid ," brakingStartReached=", brakingStartReached," tractionDeficit=", tractionDeficit," targetPositionReached=", targetPositionReached) - - if speedIsValid && !brakingStartReached && !tractionDeficit && !targetPositionReached - # 03/04 old: if drivingCourse[end][:v]>0.0 && drivingCourse[end][:v]<=CS[:v_peak] && !brakingStartReached && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - BS = createBehaviorSection(cruisingType, drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - # TODO: necessary? - s_cruising = min(s_cruising, CS[:s_exit]-BS[:s_entry]) - - # traction effort and resisting forces (in N) -#03/25 calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - if !trainIsBrakingDownhill - calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - else - calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - end - - if settings.massModel == :homogeneous_strip && CS[:id] > 1 - # conditions for cruising section - trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 -# targetSpeedReached = stateFlags[:speedLimitReached] || drivingCourse[end][:v] >= CS[:v_peak] - # TODO: change? to correctCruisingType = (trainIsClearing || (trainIsBrakingDownhill == drivingCourse[end][:F_R] < 0)) # while clearing tractive or braking force can be used -#&& targetSpeedReached - # use the conditions for the cruising section - while trainInPreviousCS && !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - currentStepSize = settings.stepSize - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation - while trainInPreviousCS && !targetPositionReached && !pointOfInterestReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - # 03/09 old: while drivingCourse[end][:s] < CS[:s_entry] + train[:length] && drivingCourse[end][:s] < BS[:s_entry] +s_cruising && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:F_T]>=drivingCourse[end][:F_R] - # the tractive effort is lower than the resisiting forces and the train has use the highest possible effort to try to stay at v_peak OR the mass model homogeneous strip is used and parts of the train are still in former CS - #TODO: maybe just consider former CS with different path resistance? - # tractive effort (in N): -#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - if !trainIsBrakingDownhill - drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - else - drivingCourse[end][:F_T] = 0.0 - end - - - # acceleration (in m/s^2): - drivingCourse[end][:a] = 0.0 - - # create the next data point - if settings.stepVariable == :distance || settings.stepVariable == time - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - else - push!(drivingCourse, moveAStep(drivingCourse[end], position, train[:length]/(10.0^cycle), CS[:id])) # TODO which step size should be used? - end - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) -# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - #end - - # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest # POIs include s_exit as well - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - trainInPreviousCS = drivingCourse[end][:s] < CS[:s_entry] + train[:length] - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - end #while - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - currentStepSize = settings.stepSize / 10.0^cycle - - elseif !trainIsBrakingDownhill && resistingForceNegative - currentStepSize = settings.stepSize / 10.0^cycle - - elseif trainIsBrakingDownhill && !resistingForceNegative - currentStepSize = settings.stepSize / 10.0^cycle - - elseif drivingCourse[end][:s] > nextPointOfInterest - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:s] > BS[:s_entry] + s_cruising # TODO also the following? drivingCourse[end][:s] > CSs[CS[:id]][:s_entry] + train[:length])) - if settings.stepVariable == :distance - currentStepSize=BS[:s_entry] + s_cruising-drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:s] == BS[:s_entry] + s_cruising # || drivingCourse[end][:s]==CS[:s_exit] - break - - elseif drivingCourse[end][:s] >= CS[:s_entry] + train[:length] - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - break - - elseif !trainInPreviousCS - break - - else - error("ERROR at cruising section: With the step variable ",settings.stepVariable," the while loop will be left although the if cases don't apply in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") - end - - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - pointOfInterestReached = false - tractionDeficit = false - targetPositionReached = false - trainInPreviousCS = true - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - - else # if the level of approximation is reached - if drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - elseif drivingCourse[end][:s] > BS[:s_entry]+s_cruising - if BS[:type] != "clearing" - pop!(drivingCourse) - pop!(BS[:dataPoints]) - end - elseif drivingCourse[end][:s] == BS[:s_entry]+s_cruising - break - elseif drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - break - elseif !trainIsBrakingDownhill && resistingForceNegative - break - elseif trainIsBrakingDownhill && !resistingForceNegative - break - elseif !trainInPreviousCS - break - - else - - end - end - end #for - end #while - end #if - - # conditions for the next while cycle - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - resistingForceNegative = drivingCourse[end][:F_R] < 0.0 - - while !targetPositionReached && !tractionDeficit && (trainIsClearing || (trainIsBrakingDownhill == resistingForceNegative)) # while clearing tractive or braking force can be used - # 03/09 old: while drivingCourse[end][:s] < BS[:s_entry]+s_cruising && drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - nextPointOfInterest = min(BS[:s_entry]+s_cruising, getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s])) - - # tractive effort (in N): -#03/25 drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - if !trainIsBrakingDownhill - drivingCourse[end][:F_T] = min(drivingCourse[end][:F_T], max(0.0, drivingCourse[end][:F_R])) - else - drivingCourse[end][:F_T] = 0.0 - end - - - - drivingCourse[end][:a] = 0.0 # acceleration (in m/s^2) - - # calculate the remaining cruising way - #s_cruisingRemaining=BS[:s_entry] + s_cruising-drivingCourse[end][:s] - s_cruisingRemaining = min(nextPointOfInterest -drivingCourse[end][:s], BS[:s_entry] +s_cruising -drivingCourse[end][:s]) - - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], :distance, s_cruisingRemaining, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) -# calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #if !trainIsBrakingDownhill - # calculateForces!(drivingCourse[end], CSs, CS[:id], "cruising", train, settings.massModel) - #else - # calculateForces!(drivingCourse[end], CSs, CS[:id], "braking", train, settings.massModel) - #end - - # conditions for the next while cycle - targetPositionReached = drivingCourse[end][:s] >= BS[:s_entry] +s_cruising - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - resistingForceNegative = drivingCourse[end][:F_R] < 0 - end #while - - # TODO: realize this better inside the upper loops? - - - # calculate the accumulated cruising section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end # else: return the characteristic section without a cruising section - - # set state flags - stateFlags[:endOfCSReached] = drivingCourse[end][:s] == CS[:s_exit] - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - stateFlags[:brakingStartReached] = brakingStartReached || drivingCourse[end][:s] + s_braking >= CS[:s_exit] - stateFlags[:tractionDeficit] = tractionDeficit - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0.0 - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - stateFlags[:error] = !(targetPositionReached || tractionDeficit || !(cruisingType == "clearing" || ((cruisingType == "downhillBraking") == resistingForceNegative))) - - return (CS, drivingCourse, stateFlags) -end #function addCruisingSection! - - -## This function calculates the data points for diminishing run when using maximum tractive effort and still getting slower -function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - calculateForces!(drivingCourse[end], CSs, CS[:id], "diminishing", train, settings.massModel) - - if haskey(stateFlags, :usedForDefiningCharacteristics) && stateFlags[:usedForDefiningCharacteristics] - ignoreBraking = true - s_braking = 0.0 - else - ignoreBraking = false - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - - # conditions for diminishing section - targetSpeedReached = drivingCourse[end][:v] <= 0.0 - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] #|| stateFlags[:tractionDeficit] - #s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - - # use the conditions for the diminishing section - if tractionDeficit && !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("diminishing", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached - currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation - while tractionDeficit && !brakingStartReached && !pointOfInterestReached && !targetSpeedReached - # 03/09 old: while drivingCourse[end][:F_T] < drivingCourse[end][:F_R] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest && drivingCourse[end][:v]>0.0 # as long as s_i + s_braking < s_end - # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) - - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # conditions for the next while cycle - if !ignoreBraking - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - end - brakingStartReached = drivingCourse[end][:s] +s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - targetSpeedReached = drivingCourse[end][:v] <= 0.0 - tractionDeficit = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - endOfCSReached = drivingCourse[end][:s] == CS[:s_exit] - end #while - - if CS[:id]==0 - testFlag = true - else - testFlag = false # for testing - end - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:v] < 0.0 - if settings.stepVariable == velocity - currentStepSize = drivingCourse[end-1][:v] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," > F_R=",drivingCourse[end][:F_R]) # for testing - currentStepSize = settings.stepSize / 10.0^cycle - - elseif s_braking > 0.0 && drivingCourse[end][:s] + s_braking > CS[:s_exit] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings.stepSize / 10.0^cycle - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPOI=",nextPointOfInterest) # for testing - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," == nextPOI=",nextPointOfInterest) # for testing - break - - elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," == F_R=",drivingCourse[end][:F_R]) # for testing - break - - elseif drivingCourse[end][:v] == 0.0 - error("ERROR: The train stops during diminishing run in CS",CS[:id]," at position s=",drivingCourse[end][:s]," m because the maximum tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", - " F_T=",drivingCourse[end-1][:F_T]," N R_traction=",drivingCourse[end-1][:R_traction]," N R_wagons=",drivingCourse[end-1][:R_wagons]," N R_path=",drivingCourse[end-1][:R_path]," N.") - - else - error("ERROR during diminishing run: With the step variable ",settings.stepVariable," the while loop will be left although s+s_braking0.0 in CS",CS[:id]," with s=" ,drivingCourse[end][:s]," m and v=",drivingCourse[end][:v]," m/s") - end - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - brakingStartReached = false - pointOfInterestReached = false - targetSpeedReached = false - tractionDeficit = true - endOfCSReached = false - - else # if the level of approximation is reached - if drivingCourse[end][:v] <= 0.0 - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: v=", drivingCourse[end][:v]," <= 0.0") # for testing - # push!(BS[:dataPoints], drivingCourse[end][:i]) - error("ERROR: The train stops during diminishing run in CS",CS[:id]," because the maximum tractive effort is lower than the resistant forces.", - " Before the stop the last point has the values s=",drivingCourse[end-1][:s]," m v=",drivingCourse[end-1][:v]," m/s a=",drivingCourse[end-1][:a]," m/s^2", - " F_T=",drivingCourse[end-1][:F_T]," N R_traction=",drivingCourse[end-1][:R_traction]," N R_wagons=",drivingCourse[end-1][:R_wagons]," N R_path=",drivingCourse[end-1][:R_path]," N.") - - elseif s_braking > 0.0 && drivingCourse[end][:s] + s_braking > CS[:s_exit] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - pointOfInterestReached = false - targetSpeedReached = false - tractionDeficit = true - endOfCSReached = false - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - - elseif drivingCourse[end][:F_T] >= drivingCourse[end][:F_R] - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: F_T=", drivingCourse[end][:F_T]," >= F_R=", drivingCourse[end][:F_R]) # for testing - break - - else - testFlag && println("in CS",CS[:id]," diminishing cycle",cycle," case: else with v=", drivingCourse[end][:v]," > 0.0 and F_T=", drivingCourse[end][:F_T]," <= F_R=", drivingCourse[end][:F_R]) # for testing - #println(" and s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," <= s_exit=",CS[:s_exit]) # for testing - #println(" and s=", drivingCourse[end][:s]," <= nextPointOfInterest=",nextPointOfInterest) # for testing - - # if drivingCourse[end][:s] + s_braking == CS[:s_exit] - # brakingStartReached = true - # end - end #if - - # # TODO is it possible to put this into to the if-fork? - # if drivingCourse[end][:s] == CS[:s_exit] - # endOfCSReached = true - # end - end #if - end #for - end #while - - if length(BS[:dataPoints]) > 1 # TODO: necessary? May it be possible that there is no diminishing because braking has to start? - # calculate the accumulated diminishing section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - mergeBehaviorSection!(CS[:behaviorSections], BS) - end - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = tractionDeficit - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_peak] - stateFlags[:error] = !(endOfCSReached || brakingStartReached || !tractionDeficit) - - return (CS, drivingCourse, stateFlags) -end #function addDiminishingSection! - - -## This function calculates the data points of the coasting section. -# Therefore it gets its previous driving course and the characteristic section and returns the characteristic section and driving course including the coasting section -function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # TODO: if the rear of the train is still located in a former characteristic section it has to be checked if its speed limit can be kept - # with getCurrentSpeedLimit - - # conditions for coasting section - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] || stateFlags[:brakingStartReached] - - # use the conditions for the coasting section - if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("coasting", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while !targetSpeedReached && !endOfCSReached && !brakingStartReached - currentStepSize=settings.stepSize # initialize the step size that can be reduced near intersections - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation - while !targetSpeedReached && !brakingStartReached && !pointOfInterestReached - # 03/09 old : while drivingCourse[end][:v] > CS[:v_exit] && drivingCourse[end][:v] <= CS[:v_peak] && !brakingStartReached && drivingCourse[end][:s] < nextPointOfInterest - # traction effort and resisting forces (in N): - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # acceleration (in m/s^2): - drivingCourse[end][:a] = calcAcceleration(drivingCourse[end][:F_T], drivingCourse[end][:F_R], train[:m_train], train[:ξ_train]) - - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - - # conditions for the next while cycle - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - brakingStartReached = drivingCourse[end][:s] + s_braking >= CS[:s_exit] - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] || drivingCourse[end][:v] > CS[:v_peak] - end # while - - testFlag = false - - # check which limit was reached and adjust the currentStepSize for the next cycle - if cycle < settings.approxLevel+1 - if drivingCourse[end][:s] + s_braking > CS[:s_exit] - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," > s_exit=",CS[:s_exit]) # for testing - currentStepSize = settings.stepSize / 10.0^cycle - - elseif drivingCourse[end][:s] > nextPointOfInterest - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s=", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - - elseif drivingCourse[end][:v] < CS[:v_exit] # TODO: if accelereation and coasting functions will be combined this case is only for coasting - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," < v_exit=", CS[:v_exit]) # for testing - if settings.stepVariable == velocity - currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:v] > CS[:v_peak] - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," > v_peak=", CS[:v_peak]) # for testing - if settings.stepVariable == velocity - currentStepSize = CS[:v_peak] - drivingCourse[end-1][:v] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:s] + s_braking == CS[:s_exit] - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s +s_braking=", drivingCourse[end][:s],"+",s_braking," = ",drivingCourse[end][:s] +s_braking," == s_exit=",CS[:s_exit]) # for testing - break - - elseif drivingCourse[end][:v] == CS[:v_exit] - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: v=", drivingCourse[end][:v]," == v_exit=", CS[:v_exit]) # for testing - break - - elseif drivingCourse[end][:s] == nextPointOfInterest - testFlag && println("in CS",CS[:id]," coasting cycle",cycle," case: s =", drivingCourse[end][:s]," > nextPointOfInterest=",nextPointOfInterest) # for testing - break - - else - # TODO: not needed. just for testing - error("ERROR at coasting until braking section: With the step variable ",settings.stepVariable," the while loop will be left although v CS[:s_exit] - # delete last data point because it went to far - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - # brakingStartReached = true - pointOfInterestReached = false - targetSpeedReached = false - - elseif drivingCourse[end][:v] > CS[:v_peak] # if the train gets to fast it has to brake # TODO: if accelereation and coasting functions will be combined this case is different for coasting and also the order of if cases is different - # delete last data point because it went to far - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - brakingStartReached = false - pointOfInterestReached = false - # targetSpeedReached = true - - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - else - # do nothing for example for drivingCourse[end][:s] + s_braking == CS[:s_exit] - end - end - end #for - end #while - - stateFlags[:speedLimitReached] = false - - # calculate the accumulated coasting section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:coasting=>BS)) - end - - # set state flags - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:brakingStartReached] = brakingStartReached - stateFlags[:tractionDeficit] = drivingCourse[end][:F_T] < drivingCourse[end][:F_R] - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - stateFlags[:error] = !(endOfCSReached || brakingStartReached || stateFlags[:tractionDeficit] || previousSpeedLimitReached || targetSpeedReached) - - return (CS, drivingCourse, stateFlags) -end #function addCoastingSection! - - -## This function calculates the data points of the braking section. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the behavior section for braking if needed. -function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::Dict, settings::Settings, train::Dict, CSs::Vector{Dict}) - # conditions for braking section - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] || stateFlags[:endOfCSReached] - - # use the conditions for the braking section - if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - drivingCourse[end][:behavior] = BS[:type] - - while !targetSpeedReached && !endOfCSReached - currentStepSize = settings.stepSize # initialize the step size that can be reduced near intersections - nextPointOfInterest = getNextPointOfInterest(CS[:pointsOfInterest], drivingCourse[end][:s]) - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - - for cycle in 1:settings.approxLevel+1 # first cycle with normal step size followed by cycles with reduced step size depending on the level of approximation - while !targetSpeedReached && !endOfCSReached && !pointOfInterestReached - # 03/09 old: while drivingCourse[end][:v] > CS[:v_exit] && !targetSpeedReached && drivingCourse[end][:s] < CS[:s_exit] && drivingCourse[end][:s] < nextPointOfInterest - # traction effort and resisting forces (in N): - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - # acceleration (in m/s^2): - drivingCourse[end][:a] = train[:a_braking] - # TODO or: drivingCourse[end][:a] = calcBrakingAcceleration(drivingCourse[end][:v], CS[:v_exit], CS[:s_exit]-drivingCourse[end][:s]) - - if settings.stepVariable == :distance && ((drivingCourse[end][:v]/drivingCourse[end][:a])^2+2*currentStepSize/drivingCourse[end][:a])<0.0 || (drivingCourse[end][:v]^2+2*currentStepSize*drivingCourse[end][:a])<0.0 - # create empty data point and set it for the values of s_exit and v_exit - push!(drivingCourse, createDataPoint()) - drivingCourse[end][:i] = drivingCourse[end-1][:i]+1 - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - else - # create the next data point - push!(drivingCourse, moveAStep(drivingCourse[end], settings.stepVariable, currentStepSize, CS[:id])) - drivingCourse[end][:behavior] = BS[:type] - push!(BS[:dataPoints], drivingCourse[end][:i]) - end - #println(drivingCourse[end][:i],". s=",drivingCourse[end][:s]," s_exit=", CS[:s_exit]," v_exit=", CS[:v_exit]," v=",drivingCourse[end][:v]) - - # conditions for the next while cycle - pointOfInterestReached = drivingCourse[end][:s] >= nextPointOfInterest - endOfCSReached = drivingCourse[end][:s] >= CS[:s_exit] - targetSpeedReached = drivingCourse[end][:v] <= CS[:v_exit] - end # while - - # check which limit was reached and adjust the currentStepSize for the next cycle - # TODO: is there a better way than rounding like in the following? - if cycle < settings.approxLevel+1 - if drivingCourse[end][:v] < CS[:v_exit] - if settings.stepVariable == velocity - currentStepSize = drivingCourse[end-1][:v] - CS[:v_exit] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:s] > nextPointOfInterest - if settings.stepVariable == :distance - currentStepSize = nextPointOfInterest - drivingCourse[end-1][:s] - else - currentStepSize = settings.stepSize / 10.0^cycle - end - elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] - break - elseif drivingCourse[end][:v] == CS[:v_exit] - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - # println(" with a=", drivingCourse[end-1][:a]) # for testing - break - elseif drivingCourse[end][:s] == CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - targetSpeedReached = true - # println(" with a=", drivingCourse[end-1][:a]) # for testing - break - elseif drivingCourse[end][:s] == nextPointOfInterest - break - end - - # delete last data point for recalculating the last step with reduced step size - pop!(drivingCourse) - pop!(BS[:dataPoints]) - - # conditions for the next for cycle - pointOfInterestReached = false - endOfCSReached = false - targetSpeedReached = false - - else # if the level of approximation is reached - if drivingCourse[end][:v] < 0.0 - # TODO: drivingCourse[end][:v] < CS[:v_exit] should be enough - # reset last point with setting v=v_exit - # println("during braking section in CS",CS[:id],": rounding v up from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], 0.0) - endOfCSReached = true - break - elseif drivingCourse[end][:s] > CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding s down from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - # recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - drivingCourse[end][:s] = CS[:s_exit] - break - elseif drivingCourse[end][:s] > nextPointOfInterest - drivingCourse[end][:s] = nextPointOfInterest # round s down to nextPointOfInterest - drivingCourse[end][:Δs] = drivingCourse[end][:s] - drivingCourse[end-1][:s] - break - elseif drivingCourse[end][:v] == CS[:v_exit] && drivingCourse[end][:s] == CS[:s_exit] - break - elseif drivingCourse[end][:v] < CS[:v_exit] - # reset last point with setting v=v_exit - # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - break - elseif drivingCourse[end][:v] == CS[:v_exit] - # println("during braking section in CS",CS[:id],": rounding s up from ", drivingCourse[end][:s] ," to ", CS[:s_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - endOfCSReached = true - break - elseif drivingCourse[end][:s] == CS[:s_exit] - # println("during braking section in CS",CS[:id],": rounding v down from ", drivingCourse[end][:v] ," to ", CS[:v_exit]) # for testing - recalculateLastBrakingPoint!(drivingCourse, CS[:s_exit], CS[:v_exit]) - targetSpeedReached = true - break - else - # do nothing for example for drivingCourse[end][:s]==nextPointOfInterest - end - end - end #for - end #while - - # calculate the accumulated coasting section information - merge!(BS, Dict(:length => drivingCourse[end][:s] - BS[:s_entry], # total length (in m) - :s_exit => drivingCourse[end][:s], # last position (in m) - :t => drivingCourse[end][:t] - drivingCourse[BS[:dataPoints][1]][:t], # total running time (in s) - :E => drivingCourse[end][:E] - drivingCourse[BS[:dataPoints][1]][:E], # total energy consumption (in Ws) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - - CS[:t] = CS[:t] + BS[:t] # total running time (in s) - CS[:E] = CS[:E] + BS[:E] # total energy consumption (in Ws) - - merge!(CS[:behaviorSections], Dict(:braking=>BS)) - end # else: return the characteristic section without a braking section - - # set state flags - currentSpeedLimit = getCurrentSpeedLimit(CSs, CS[:id], drivingCourse[end][:s], train[:length]) - stateFlags[:previousSpeedLimitReached] = currentSpeedLimit[:v] != CS[:v_limit] && drivingCourse[end][:v] >= currentSpeedLimit[:v] - stateFlags[:speedLimitReached] = drivingCourse[end][:v] >= CS[:v_exit] - stateFlags[:endOfCSReached] = endOfCSReached - stateFlags[:error] = !(endOfCSReached) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) - stateFlags[:resistingForceNegative] = drivingCourse[end][:F_R] < 0 - - return (CS, drivingCourse, stateFlags) -end #function addBrakingSection! - - -## This function calculates the data point of the standstill. -# Therefore it gets its first data point and the characteristic section and returns the characteristic section including the standstill if needed. -function addStandstill!(CS::Dict, drivingCourse::Vector{Dict}, settings::Settings, train::Dict, CSs::Vector{Dict}) - if drivingCourse[end][:v] == 0.0 - BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) - merge!(BS, Dict(:length => 0.0, # total length (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :s_exit => drivingCourse[end][:s], # last position (in m) - :v_exit => drivingCourse[end][:v])) # exit speed (in m/s))) - drivingCourse[end][:behavior] = BS[:type] - - # traction effort and resisting forces (in N) - calculateForces!(drivingCourse[end], CSs, CS[:id], BS[:type], train, settings.massModel) - - merge!(CS[:behaviorSections], Dict(:standstill => BS)) - end # else: return the characteristic section without a standstillSection section - return (CS, drivingCourse) -end #function addStandstill! - -function mergeBehaviorSection!(BSs::Dict, BS::Dict) - if !haskey(BSs, Symbol(BS[:type])) - merge!(BSs, Dict(Symbol(BS[:type]) => BS)) - else - number = "2" - while haskey(BSs, Symbol(BS[:type]*number)) - number = string(parse(Int, number)+1) - end - merge!(BSs, Dict(Symbol(BS[:type]*number) => BS)) - # println("INFO: The ",number,". ",BS[:type]," section has been created. ! ! ! ! ! ! ! ! !") - end - return BSs -end #function mergeBehaviorSection! - -function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) - BS= Dict(#:type => behavior, # type of behavior section: breakFree, clearing, accelerating, cruising, diminishing, coasting, braking or standstill - :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" - :length => 0.0, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => 0.0, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_entry => v_entry, # entry speed (in m/s) - :v_exit => 0.0, # exit speed (in m/s) - :dataPoints => [startingPoint]) # list of identifiers of the containing data points starting with the initial point - return BS -end #function createBehaviorSection - -""" -a data point is the smallest element of the driving course. One step of the step approach is between two data points -""" -function createDataPoint() - dataPoint = Dict(:i => 0, # identifier and counter variable of the dricing course - :behavior => "", # type of behavior section the data point is part of ("breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") - # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter - :s => 0.0, # position (in m) - :Δs => 0.0, # step size (in m) - :t => 0.0, # point in time (in s) - :Δt => 0.0, # step size (in s) - :v => 0.0, # velocity (in m/s) - :Δv => 0.0, # step size (in m/s) - :a => 0.0, # acceleration (in m/s^2) - :W => 0.0, # mechanical work (in Ws) - :ΔW => 0.0, # mechanical work in this step (in Ws) - :E => 0.0, # energy consumption (in Ws) - :ΔE => 0.0, # energy consumption in this step (in Ws) - :F_T => 0.0, # tractive effort (in N) - :F_R => 0.0, # resisting force (in N) - :R_path => 0.0, # path resistance (in N) - :R_train => 0.0, # train resistance (in N) - :R_traction => 0.0, # traction unit resistance (in N) - :R_wagons => 0.0) # set of wagons resistance (in N) - return dataPoint -end #function createDataPoint - -function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) - currentPoint = drivingCourse[end] - previousPoint = drivingCourse[end-1] - # set s and v - currentPoint[:s] = s_target # position (in m) - currentPoint[:v] = v_target # velocity (in m/s) - currentPoint[:Δs] = currentPoint[:s] - previousPoint[:s] # step size (in m) - currentPoint[:Δv] = currentPoint[:v] - previousPoint[:v] # step size (in m/s) - - # calculate other values - previousPoint[:a] = calcBrakingAcceleration(previousPoint[:v], currentPoint[:v], currentPoint[:Δs]) -# # TODO: just for testing -# if previousPoint[:a]=0.0 -# println("Warning: a_braking gets to high in CS ",CS[:id], " with a=",previousPoint[:a] ," > ",train[:a_braking]) -# end - currentPoint[:Δt] = calc_Δt_with_Δv(currentPoint[:Δv], previousPoint[:a]) # step size (in s) - currentPoint[:t] = previousPoint[:t] + currentPoint[:Δt] # point in time (in s) - - currentPoint[:ΔW] = 0.0 # mechanical work in this step (in Ws) - currentPoint[:W] = previousPoint[:W] + currentPoint[:ΔW] # mechanical work (in Ws) - currentPoint[:ΔE] = currentPoint[:ΔW] # energy consumption in this step (in Ws) - currentPoint[:E] = previousPoint[:E] + currentPoint[:ΔE] # energy consumption (in Ws) -end #function recalculateLastBrakingPoint diff --git a/src/Calc.jl b/src/Calc.jl deleted file mode 100644 index 3ba2662..0000000 --- a/src/Calc.jl +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -# Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding YAML files with the file paths `trainDirectory`, `pathDirectory`, `settingsDirectory`. - -""" - trainRun(train::Dict, path::Dict, settings::Settings) - -Calculate the driving dynamics of a train run on a path with special settings with information from the corresponding dictionaries `train`, `path`, `settings`. - -# Examples -```julia-repl -julia> trainRun(trainDict, pathDict) -todo !!! -``` -""" -function trainRun(trainInput::Dict, pathInput::Dict, settings=Settings()::Settings) - # copy Input data for not changing them - # TODO: or should they be changed? normally it would only make it "better" except for settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) - train = copy(trainInput) - path = copy(pathInput) - - # check the input data - (train, path) = checkAndSetInput!(train, path, settings) - settings.outputDetail == :everything && println("The input has been checked.") - - # prepare the input data - movingSection = determineCharacteristics(path, train, settings) - settings.outputDetail == :everything && println("The moving section has been prepared.") - - # calculate the train run for oparation mode "minimum running time" - (movingSection, drivingCourse) = calculateMinimumRunningTime!(movingSection, settings, train) - settings.outputDetail == :everything && println("The driving course for the shortest running time has been calculated.") - - # accumulate data and create an output dictionary - output = createOutput(train, settings, path, movingSection, drivingCourse) - - return output -end # function trainRun - -# calculate a train run focussing on using the minimum possible running time -function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, train::Dict) - CSs::Vector{Dict} = movingSection[:characteristicSections] - - if settings.massModel == :homogeneous_strip && settings.stepVariable == speed - println("WARNING: ! ! ! TrainRun.jl doesn't work reliably for the mass model homogeneous strip with step size v in m/s. The calculation time can be extremely high when calcutlating paths with steep gradients ! ! !") - end - - startingPoint=createDataPoint() - startingPoint[:i]=1 - startingPoint[:s]=CSs[1][:s_entry] - calculateForces!(startingPoint, CSs, 1, "default", train, settings.massModel) # traction effort and resisting forces (in N) - drivingCourse::Vector{Dict} = [startingPoint] # List of data points - - for csId in 1:length(CSs) - CS = CSs[csId] - # for testing - if drivingCourse[end][:s] != CS[:s_entry] - println("ERROR: In CS", csId," the train run starts at s=",drivingCourse[end][:s]," and not s_entry=",CS[:s_entry]) - end - if drivingCourse[end][:v] > CS[:v_entry] - println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_entry=",CS[:v_entry]) - end - - # determine the different flags for switching between the states for creatinge moving phases - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - calculateForces!(drivingCourse[end], CSs, CS[:id], "default", train, settings.massModel) # tractive effort and resisting forces (in N) - - previousSpeedLimitReached = false - stateFlags = Dict(:endOfCSReached => drivingCourse[end][:s] > CS[:s_exit], - :brakingStartReached => drivingCourse[end][:s] + s_braking == CS[:s_exit], - :tractionDeficit => drivingCourse[end][:F_T] < drivingCourse[end][:F_R], # or add another flag for equal forces? - :resistingForceNegative => drivingCourse[end][:F_R] < 0.0, - :previousSpeedLimitReached => false, #speedLimitReached, # check already at this position? - :speedLimitReached => drivingCourse[end][:v] > CS[:v_limit], - :error => false) - - # determine the behavior sections for this characteristic section. It has to be at least one of those BS: "breakFree", "clearing", "accelerating", "cruising", "diminishing", "coasting", "braking" or "standstill") - while !stateFlags[:endOfCSReached] # s < s_exit - if !stateFlags[:brakingStartReached] # s+s_braking < s_exit - if !stateFlags[:tractionDeficit] - if drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && drivingCourse[end][:v] == 0.0 - (CS, drivingCourse, stateFlags) = addBreakFreeSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif stateFlags[:previousSpeedLimitReached] - (CS, drivingCourse, stateFlags) = addClearingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif drivingCourse[end][:F_T] > drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] - (CS, drivingCourse, stateFlags) = addAcceleratingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] && !stateFlags[:speedLimitReached] - # cruise only one step - if settings.stepVariable == :distance - s_cruising = settings.stepSize - elseif settings.stepVariable == time - s_cruising = calc_Δs_with_Δt(settings.stepSize, drivingCourse[end][:a], drivingCourse[end][:v]) - elseif settings.stepVariable == velocity - s_cruising = train[:length]/(10.0) # TODO which step size should be used? - end - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") - - elseif drivingCourse[end][:F_R] < 0 && stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking - - if s_cruising > 0.0 - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") - else - stateFlags[:brakingStartReached] = true - end - - elseif drivingCourse[end][:F_T] == drivingCourse[end][:F_R] || stateFlags[:speedLimitReached] - s_braking = calcBrakingDistance(drivingCourse[end][:v], CS[:v_exit], train[:a_braking]) - s_cruising = CS[:s_exit] - drivingCourse[end][:s] - s_braking - - if s_cruising > 0.0 # TODO: define a minimum cruising length? - (CS, drivingCourse, stateFlags) = addCruisingSection!(CS, drivingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") - else - stateFlags[:brakingStartReached] = true - end - else - error() - end - elseif stateFlags[:tractionDeficit] - (CS, drivingCourse, stateFlags) = addDiminishingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - - else - error() - end - else#if !stateFlags[:endOfCSReached] # s < s_exit - (CS, drivingCourse, stateFlags) = addBrakingSection!(CS, drivingCourse, stateFlags, settings, train, CSs) - #else - # error() - end - end - #if s == s_exit - # standstill - #end - - - # for testing: - if drivingCourse[end][:s] != CS[:s_exit] - println("ERROR: In CS", csId," the train run ends at s=",drivingCourse[end][:s]," and not s_exit=",CS[:s_exit]) - end - if drivingCourse[end][:v] > CS[:v_exit] - println("ERROR: In CS", csId," the train run ends with v=",drivingCourse[end][:v]," and not with v_exit=",CS[:v_exit]) - end - end #for - - (CSs[end], drivingCourse) = addStandstill!(CSs[end], drivingCourse, settings, train, CSs) - - movingSection[:t] = drivingCourse[end][:t] # total running time (in s) - movingSection[:E] = drivingCourse[end][:E] # total energy consumption (in Ws) - - return (movingSection, drivingCourse) -end #function calculateMinimumRunningTime diff --git a/src/Characteristics.jl b/src/Characteristics.jl deleted file mode 100644 index 4dd83c2..0000000 --- a/src/Characteristics.jl +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior -function determineCharacteristics(path::Dict, train::Dict, settings::Settings) - movingSection = createMovingSection(path, train[:v_limit]) - movingSection = secureBrakingBehavior!(movingSection, train[:a_braking]) - movingSection = secureAcceleratingBehavior!(movingSection, settings, train) - #movingSection = secureCruisingBehavior!(movingSection, settings, train) - - return movingSection -end #function determineCharacteristics - -## create a moving section containing characteristic sections -function createMovingSection(path::Dict, v_trainLimit::Real) - # this function creates and returns a moving section dependent on the paths attributes - - s_entry = path[:sections][1][:s_start] # first position (in m) - s_exit = path[:sections][end][:s_end] # last position (in m) - pathLength = s_exit - s_entry # total length (in m) - - CSs=Vector{Dict}() - s_csStart=s_entry - csId=1 - for row in 2:length(path[:sections]) - previousSection = path[:sections][row-1] - currentSection = path[:sections][row] - speedLimitIsDifferent = min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) - pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] - if speedLimitIsDifferent || pathResistanceIsDifferent - # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] - push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), path)) - s_csStart = currentSection[:s_start] - csId = csId+1 - end #if - end #for - push!(CSs, createCharacteristicSection(csId, s_csStart, path[:sections][end], min(path[:sections][end][:v_limit], v_trainLimit), path)) - - movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore - :length => pathLength, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => s_exit, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :characteristicSections => CSs) # list of containing characteristic sections - - return movingSection -end #function createMovingSection - - -## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. -function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, path::Dict) - # Create and return a characteristic section dependent on the paths attributes - characteristicSection= Dict(:id => id, # identifier - :s_entry => s_entry, # first position (in m) - :s_exit => section[:s_end], # last position (in m) - :length => section[:s_end] -s_entry, # total length (in m) - :r_path => section[:f_Rp], # path resistance (in ‰) - :behaviorSections => Dict(), # list of containing behavior sections - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_limit => v_limit, # speed limit (in m/s) - # initializing :v_entry, :v_peak and :v_exit with :v_limit - :v_peak => v_limit, # maximum reachable speed (in m/s) - :v_entry => v_limit, # maximum entry speed (in m/s) - :v_exit => v_limit) # maximum exit speed (in m/s) - - # list of positions of every point of interest (POI) in this charateristic section for which data points should be calculated - s_exit = characteristicSection[:s_exit] - pointsOfInterest = Vector{Real}() - if haskey(path, :pointsOfInterest) - for POI in path[:pointsOfInterest] - if s_entry < POI && POI < s_exit - push!(pointsOfInterest, POI) - end - end - end - push!(pointsOfInterest, s_exit) # s_exit has to be the last POI so that there will always be a POI to campare the current position with - - merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) - - return characteristicSection -end #function createCharacteristicSection - -## define the intersection velocities between the characterisitc sections to secure braking behavior -function secureBrakingBehavior!(movingSection::Dict, a_braking::Real) - # this function limits the entry and exit velocity of the characteristic sections to secure that the train stops at the moving sections end - CSs = movingSection[:characteristicSections] - - csId = length(CSs) - followingCSv_entry = 0.0 # the exit velocity of the last characteristic section is 0.0 m/s - while csId >= 1 - CS = CSs[csId] - - CS[:v_exit] = min(CS[:v_limit], followingCSv_entry) - - v_entryMax = calcBrakingStartVelocity(CS[:v_exit], a_braking, CS[:length]) - - CS[:v_entry] = min(CS[:v_limit], v_entryMax) - CS[:v_peak] = CS[:v_entry] - - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - - followingCSv_entry = CS[:v_entry] - csId = csId - 1 - end #while - return movingSection -end #function secureBrakingBehavior! - -## define the intersection velocities between the characterisitc sections to secure accelerating behavior -function secureAcceleratingBehavior!(movingSection::Dict, settings::Settings, train::Dict) - # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards - CSs = movingSection[:characteristicSections] - - CSs[1][:v_entry] = 0.0 # the entry velocity of the first characteristic section is 0.0 m/s - startingPoint = createDataPoint() - startingPoint[:i] = 1 - - previousCSv_exit = CSs[1][:v_entry] - for CS in CSs - CS[:v_entry] = min(CS[:v_entry], previousCSv_exit) - startingPoint[:s] = CS[:s_entry] - startingPoint[:v] = CS[:v_entry] - calculateForces!(startingPoint, CSs, CS[:id], "accelerating", train, settings.massModel) # traction effort and resisting forces (in N) - acceleratingCourse::Vector{Dict} = [startingPoint] # List of data points - - if CS[:v_entry] < CS[:v_peak] - # conditions for entering the accelerating phase - stateFlags = Dict(:endOfCSReached => false, - :brakingStartReached => false, - :tractionDeficit => false, - :resistingForceNegative => false, - :previousSpeedLimitReached => false, - :speedLimitReached => false, - :error => false, - :usedForDefiningCharacteristics => true) # because usedForDefiningCharacteristics == true the braking distance will be ignored during securing the accelerating phase - v_peak = CS[:v_entry] - (CS, acceleratingCourse, stateFlags) = addBreakFreeSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) - while !stateFlags[:speedLimitReached] && !stateFlags[:endOfCSReached] - if !stateFlags[:tractionDeficit] - if !stateFlags[:previousSpeedLimitReached] - (CS, acceleratingCourse, stateFlags) = addAcceleratingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function changes the acceleratingCourse - - elseif stateFlags[:previousSpeedLimitReached] - (CS, acceleratingCourse, stateFlags) = addClearingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the train is not allowed to accelerate because of a previous speed limit - end - else - if settings.massModel == :mass_point || acceleratingCourse[end][:s] > CS[:s_entry] + train[:length] - break - else - (CS, acceleratingCourse, stateFlags) = addDiminishingSection!(CS, acceleratingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort - end - end - v_peak = max(v_peak, acceleratingCourse[end][:v]) - end - -# CS[:v_peak] = max(CS[:v_entry], acceleratingCourse[end][:v]) - CS[:v_peak] = v_peak - CS[:v_exit] = min(CS[:v_exit], CS[:v_peak], acceleratingCourse[end][:v]) - else #CS[:v_entry] == CS[:v_peak] - # v_exit stays the same - end #if - - previousCSv_exit = CS[:v_exit] - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - end #for - - return movingSection -end #function secureAcceleratingBehavior! - - -#= -## define the intersection velocities between the characterisitc sections to secure cruising behavior -function secureCruisingBehavior!(movingSection::Dict, settings::Settings, train::Dict) - # limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak - CSs = movingSection[:characteristicSections] - - startingPoint = createDataPoint() - startingPoint[:i] = 1 - - previousCSv_exit = CSs[1][:v_entry] - - for CS in CSs - # conditions for entering the cruising phase - stateFlags = Dict(:endOfCSReached => false, - :brakingStartReached => false, - :tractionDeficit => false, - :resistingForceNegative => false, - :previousSpeedLimitReached => false, - :speedLimitReached => false, - :error => false, - :usedForDefiningCharacteristics => true) - - CS[:v_entry] = min(CS[:v_entry], previousCSv_exit) - - startingPoint[:s] = CS[:s_entry] - startingPoint[:v] = CS[:v_peak] - cruisingCourse::Vector{Dict} = [startingPoint] # List of data points - - while !stateFlags[:endOfCSReached] #&& s_cruising > 0.0 - if !stateFlags[:tractionDeficit] - s_cruising = CS[:s_exit] - cruisingCourse[end][:s] - if !stateFlags[:resistingForceNegative]# cruisingCourse[end][:F_R] >= 0 - (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "cruising") # this function changes the cruisingCourse - else - (CS, cruisingCourse, stateFlags) = addCruisingSection!(CS, cruisingCourse, stateFlags, s_cruising, settings, train, CSs, "downhillBraking") - end - else - if settings.massModel == :mass_point || cruisingCourse[end][:s] > CS[:s_entry] + train[:length] - break - else - (CS, cruisingCourse, stateFlags) = addDiminishingSection!(CS, cruisingCourse, stateFlags, settings, train, CSs) # this function is needed in case the resisitng forces are higher than the maximum possible tractive effort - end - end - end - - CS[:v_exit] = min(CS[:v_exit], cruisingCourse[end][:v]) - - previousCSv_exit = CS[:v_exit] - - # reset the characteristic section (CS), delete behavior sections (BS) that were used during the preperation for setting v_entry, v_peak and v_exit - CS[:behaviorSections] = Dict() - CS[:E] = 0.0 - CS[:t] = 0.0 - end #for - - return movingSection -end #function secureCruisingBehavior! -=# diff --git a/src/Export.jl b/src/Export.jl deleted file mode 100644 index 7113866..0000000 --- a/src/Export.jl +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -function exportToCsv(runningTime::AbstractFloat, settings::Settings) - createCsvFile(runningTime, settings) - - return true -end - -function exportToCsv(dataPointsToExport::Vector{Dict}, settings::Settings) - createCsvFile(dataPointsToExport, settings) - - return true -end - -function exportToCsv(output::Dict) - if output[:settings][:outputFormat] == "CSV" - pathName = output[:path][:name] - trainName = output[:train][:name] - - if output[:settings][:operationModeMinimumRunningTime] == true - operationMode = "minimum running time" - if output[:settings][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumRunningTime] - else - dataPointsToExport = output[:drivingCourseMinimumRunningTime] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - if output[:settings][:operationModeMinimumEnergyConsumption] == true - operationMode = "minimum energy consumption" - if output[:settings][:outputDetail] == "points of interest" - dataPointsToExport = output[:pointsOfInterestMinimumEnergyConsumption] - else - dataPointsToExport = output[:drivingCourseMinimumEnergyConsumption] - end - createCsvFile(dataPointsToExport, operationMode, pathName, trainName, output[:settings]) - end - return true - end - return false -end #function exportToCsv - -function createCsvFile(runningTime::AbstractFloat, settings::Settings) - # create DataFrame with running time information - df = DataFrame(column1=["t (in s)", runningTime]) - - # save DataFrame as a CSV-file at outputDir - date = Dates.now() - dateString = Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - - csvFilePath = settings[:outputDir]*"/"*dateString*"_RunningTime.csv" - - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -function createCsvFile(dataPointsToExport::Vector{Dict}, settings::Settings) - outputDetail = settings[:outputDetail] - - header = ["i", "behavior", "Δs (in m)", "s (in m)", "Δt (in s)","t (in s)","Δv (in m/s)","v (in m/s)","F_T (in N)","F_R (in N)","R_path (in N)","R_train (in N)","R_traction (in N)","R_wagons (in N)", "ΔW (in Ws)","W (in Ws)","ΔE (in Ws)","E (in Ws)","a (in m/s^2)"] - columnSymbols = [:i, :behavior, :Δs, :s, :Δt, :t, :Δv, :v, :F_T, :F_R, :R_path, :R_train, :R_traction, :R_wagons, :ΔW, :W, :ΔE, :E, :a] - - allColumns = Array{Any,1}[] - for column in 1:length(header) - currentColumn = Any[] - push!(currentColumn, header[column]) - for point in dataPointsToExport - push!(currentColumn, point[columnSymbols[column]]) - end - push!(allColumns, currentColumn) - end # for - - - # combine the columns in a data frame and saving it as a CSV-file at outputDir - if outputDetail == "driving course" || outputDetail == "points of interest" - df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) - - else - println("") - end - - date = Dates.now() - dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - csvFilePath=settings[:outputDir]*"/"*dateString*"_DataPoints.csv" - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created at ",csvFilePath) - - return true -end #function createCsvFile - - -function createCsvFile(dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Settings) - outputDetail = settings[:outputDetail] - - massModel = settings.massModel - stepVariable = settings.stepVariable - stepSize = string(settings.stepSize) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - push!(accumulatedData, ["i", "behavior", "Δs (in m)", "s (in m)", "Δt (in s)","t (in s)","Δv (in m/s)","v (in m/s)","F_T (in N)","F_R (in N)","R_path (in N)","R_train (in N)","R_traction (in N)","R_wagons (in N)", "ΔW (in Ws)","W (in Ws)","ΔE (in Ws)","E (in Ws)","a (in m/s^2)"]) # push header to accumulatedData - for point in dataPointsToExport - row = [point[:i], point[:behavior], point[:Δs], point[:s], point[:Δt], point[:t], point[:Δv], point[:v], point[:F_T], point[:F_R], point[:R_path], point[:R_train], point[:R_traction], point[:R_wagons], point[:ΔW], point[:W], point[:ΔE], point[:E], point[:a]] - push!(accumulatedData, row) # push row to accumulatedData - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # combine the columns in a data frame and saving it as a CSV-file at outputDir - df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) - - date = Dates.now() - dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - if operationMode == "minimum running time" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile - - - -#= -function createCsvFile(movingSection::Dict, dataPointsToExport::Vector{Dict}, operationMode::String, pathName::String, trainName::String, settings::Settings) - outputDetail = settings[:outputDetail] - - massModel = settings.massModel - stepVariable = settings.stepVariable - stepSize = string(settings.stepSize) - - # create accumulated data block - accumulatedData = Array{Any, 1}[] - if outputDetail == "minimal" - push!(accumulatedData, ["s (in m)", "t (in s)","E (in Ws)"]) # push header to accumulatedData - row = [movingSection[:length], movingSection[:t], movingSection[:E]] - push!(accumulatedData, row) # push row to accumulatedData - elseif outputDetail == "driving course" || outputDetail == "points of interest" - push!(accumulatedData, ["i", "behavior", "Δs (in m)", "s (in m)", "Δt (in s)","t (in s)","Δv (in m/s)","v (in m/s)","F_T (in N)","F_R (in N)","R_path (in N)","R_train (in N)","R_traction (in N)","R_wagons (in N)", "ΔW (in Ws)","W (in Ws)","ΔE (in Ws)","E (in Ws)","a (in m/s^2)"]) # push header to accumulatedData - for point in dataPointsToExport - row = [point[:i], point[:behavior], point[:Δs], point[:s], point[:Δt], point[:t], point[:Δv], point[:v], point[:F_T], point[:F_R], point[:R_path], point[:R_train], point[:R_traction], point[:R_wagons], point[:ΔW], point[:W], point[:ΔE], point[:E], point[:a]] - push!(accumulatedData, row) # push row to accumulatedData - end - end - - #create information block - allColumns=Array{Any,1}[] - push!(allColumns, ["path name", "train name", "operation mode", "mass model", "step variable", "step size", ""]) - push!(allColumns, [pathName, trainName, operationMode, massModel, stepVariable, stepSize, ""]) - for column in 3:length(accumulatedData[1]) - push!(allColumns, ["", "", "", "", "", "", ""]) - end # for - - # add driving data to the array - header = accumulatedData[1] - for column in 1:length(accumulatedData[1]) - push!(allColumns[column], header[column]) - for row in accumulatedData[2:end] - push!(allColumns[column], row[column]) - end - end # for - - # combine the columns in a data frame and saving it as a CSV-file at outputDir - if outputDetail == "minimal" - df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3]) - elseif outputDetail=="driving course" || outputDetail == "points of interest" - df = DataFrame(c1=allColumns[1], c2=allColumns[2],c3=allColumns[3], c4=allColumns[4], c5=allColumns[5], c6=allColumns[6], c7=allColumns[7], c8=allColumns[8], c9=allColumns[9], c10=allColumns[10], c11=allColumns[11], c12=allColumns[12], c13=allColumns[13], c14=allColumns[14], c15=allColumns[15], c16=allColumns[16], c17=allColumns[17], c18=allColumns[18], c19=allColumns[19]) - end - - date = Dates.now() - dateString=Dates.format(date, "yyyy-mm-dd_HH.MM.SS") - if operationMode == "minimum running time" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumRunningTime.csv" - elseif operationMode == "minimum energy consumption" - csvFilePath=settings[:outputDir]*"/"*dateString*"_MinimumEnergyConsumption.csv" - else - # should not be possible - end - CSV.write(csvFilePath, df, header=false) - println("The output CSV file has been created for ",operationMode," at ",csvFilePath) - - return true -end #function createCsvFile -=# diff --git a/src/Formulary.jl b/src/Formulary.jl deleted file mode 100644 index 1be57a6..0000000 --- a/src/Formulary.jl +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2022" -# __license__ = "ISC" - -######################### -## literature the driving dynamics equations are based on: -## -## @incollection{Bruenger:2014, % Chapter 4 -## author = {Brünger, Olaf and Dahlhaus, Elias}, -## year = {2014}, -## title = {Running Time Estimation}, -## pages = {65--90}, -## booktitle = {Railway Timetabling \& Operations.}, -## editora = {Hansen, Ingo A.}, -## editorb = {Pachl, Jörn}, -## isbn = {978-3-777-10462-1}, -## publisher = {Eurailpress DVV Media Group}, -## } -## @Book{Wende:2003, -## author = {Wende, Dietrich}, -## date = {2003}, -## title = {Fahrdynamik des Schienenverkehrs}, -## isbn = {978-3-322-82961-0}, -## publisher = {Springer-Verlag}, -## } -######################### - -approxLevel = 6 -v00 = 100/3.6 # velocity factor (in m/s) -g = 9.81 # acceleration due to gravity (in m/s^2) # TODO: should more digits of g be used? g=9,80665 m/s^2 - -## calculate forces - -#TODO: replace the ? ? ? -""" - calcTractionUnitResistance(v, train) - -Calculate the vehicle resistance for the traction unit of the `train` dependend on the velocity `v`. - -... -# Arguments -- `v::AbstractFloat`: the current velocity in m/s. -- `train::Dict`: ? ? ? -... - -# Examples -```julia-repl -julia> calcTractionUnitResistance(30.0, ? ? ?) -? ? ? -``` -""" -function calcTractionUnitResistance(v::AbstractFloat, train::Dict) - # equation is based on [Wende:2003, page 151] - f_Rtd0 = train[:f_Rtd0] # coefficient for basic resistance due to the traction units driving axles (in ‰) - f_Rtc0 = train[:f_Rtc0] # coefficient for basic resistance due to the traction units carring axles (in ‰) - F_Rt2 = train[:F_Rt2] # coefficient for air resistance of the traction units (in N) - m_td = train[:m_td] # mass on the traction unit's driving axles (in kg) - m_tc = train[:m_tc] # mass on the traction unit's carrying axles (in kg) - Δv_t = train[:Δv_t] # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - - F_R_tractionUnit = f_Rtd0/1000 * m_td * g + f_Rtc0/1000 * m_tc * g + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) # /1000 because of the unit ‰ - # TODO: use calcForceFromCoefficient? F_R_tractionUnit = calcForceFromCoefficient(f_Rtd0, m_td) + calcForceFromCoefficient(f_Rtc0, m_tc) + F_Rt2 * ((v + Δv_t) /v00)^2 # vehicle resistance of the traction unit (in N) - return F_R_tractionUnit - #TODO: same variable name like in the rest of the tool? return R_traction - #TODO: just one line? return train[:f_Rtd0]/1000*train[:m_td]*g+train[:f_Rtc0]/1000*train[:m_tc]*g+train[:F_Rt2]*((v+train[:Δv_t])/v00)^2 # /1000 because of the unit ‰ -end #function calcTractionUnitResistance - -""" -TODO -calculate and return the wagons vehicle resistance dependend on the velocity -""" -function calcWagonsResistance(v::AbstractFloat, train::Dict) - # equation is based on a combination of the equations of Strahl and Sauthoff [Wende:2003, page 153] with more detailled factors (Lehmann, page 135) - f_Rw0 = train[:f_Rw0] # coefficient for basic resistance of the set of wagons (consist) (in ‰) - f_Rw1 = train[:f_Rw1] # coefficient for the consists resistance to rolling (in ‰) - f_Rw2 = train[:f_Rw2] # coefficient fo the consistsr air resistance (in ‰) - m_w = train[:m_w] # mass of the set of wagons (consist) (in kg) - Δv_w = train[:Δv_w] # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - - F_R_wagons = m_w *g *(f_Rw0/1000 + f_Rw1/1000 *v /v00 + f_Rw2/1000 * ((v + Δv_w) /v00)^2) # vehicle resistance of the wagons (in N) # /1000 because of the unit ‰ -# TODO: use calcForceFromCoefficient? F_R_wagons = calcForceFromCoefficient(f_Rw0, m_w) + calcForceFromCoefficient(f_Rw1, m_w) *v /v00 + calcForceFromCoefficient(f_Rw2, m_w) * ((v + Δv_w) /v00)^2 # vehicle resistance of the wagons (in N) - return F_R_wagons -end #function calcWagonsResistance - -function calcForceFromCoefficient(f_R::Real, m::Real) - # equation is based on [Wende:2003, page 8] - - # f_R: specific resistance (in ‰) - # m: vehicle's mass (in kg) - - F_R = f_R /1000 *m *g # Resisting Force (in N) # /1000 because of the unit ‰ - return F_R -end #function calcForceFromCoefficient - -function calcAcceleration(F_T::Real, F_R::Real, m_train::Real, ξ_train::Real) - # equation is based on [Bruenger:2014, page 72] with a=dv/dt - - # F_T: tractive effort (in N) - # F_R: resisting forces (in N) - # m_train: train's mass (in kg) - # ξ_train: train's rotation mass factor (without unit) - - a = (F_T - F_R) /m_train /ξ_train # acceleration (in m/s) - return a -end #function calcAcceleration - -function calc_Δs_with_Δt(Δt::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δt: time step (in s) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δs = Δt * (2*v_prev + Δt*a_prev) /2 # step size (in m) - return Δs -end #function calc_Δs_with_Δt - -function calc_Δs_with_Δv(Δv::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δv: velocity step (in m/s) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δs = ((v_prev + Δv)^2 - v_prev^2)/2/a_prev # step size (in m) - return Δs -end #function calc_Δs_with_Δv - -function calc_Δt_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - - Δt = sign(a_prev) *sqrt((v_prev /a_prev)^2 + 2 *Δs /a_prev) - v_prev /a_prev # step size (in m/s) - return Δt -end #function calc_Δt_with_Δs - -function calc_Δt_with_Δv(Δv::Real, a_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δv: velocity step (in m/s) - # a_prev: acceleration from previous data point - Δt = Δv /a_prev # step size (in s) - return Δt -end #function calc_Δt_with_Δv - -function calc_Δt_with_constant_v(Δs::Real, v::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # v: constant velocity (in m/s) - Δt = Δs /v # step size (in s) - return Δt -end #function calc_Δt_with_constant_v - -function calc_Δv_with_Δs(Δs::Real, a_prev::Real, v_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δs: distance step (in m) - # a_prev: acceleration from previous data point - # v_prev: velocitiy from previous data point - Δv = sqrt(v_prev^2 + 2*Δs*a_prev) - v_prev # step size (in m/s) - return Δv -end #function calc_Δv_with_Δs - -function calc_Δv_with_Δt(Δt::Real, a_prev::Real) - # equation is based on [Wende:2003, page 37] - - # Δt: time step (in s) - # a_prev: acceleration from previous data point - Δv = Δt * a_prev # step size (in m/s) - return Δv -end #function calc_Δv_with_Δt - -function calc_ΔW(F_T_prev::Real, Δs::Real) - # equation is based on [Wende:2003, page 17] - - # F_T_prev: tractive force from previous data point - # Δs: distance step - ΔW = F_T_prev * Δs # mechanical work in this step (in Ws) - return ΔW -end #function calc_ΔW - -function calc_ΔE(ΔW::Real) - # simplified equation - # TODO! - # ΔW: mechanical work in this step (in Ws) - ΔE = ΔW # energy consumption in this step (in Ws) - return ΔE -end #function calc_ΔW - -function calcBrakingDistance(v_start::Real, v_end::Real, a_braking::Real) - # equation is based on [Wende:2003, page 37] - - # v_start: velocity at the start of braking (in m/s) - # v_end: target velocity at the end of braking (in m/s) - # a_braking: constant braking acceleration (in m/s^2) - s_braking = (v_end^2 - v_start^2) /2 /a_braking # braking distance (in m) - # TODO: also possible: calc_Δs_with_Δv(v_end-v_start, a_braking, v_start) -# return max(0.0, ceil(s_braking, digits=approxLevel)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors - return max(0.0, ceil(s_braking, digits=approxLevel +1)) # ceil is used to be sure that the train stops at s_exit in spite of rounding errors -end #function calcBrakingDistance - -function calcBrakingStartVelocity(v_end::Real, a_braking::Real, s_braking::Real) - # equation is based on [Wende:2003, page 37] - - # v_end: target velocity at the end of braking (in m/s) - # a_braking: constant braking acceleration (in m/s^2) - # s_braking: braking distance (in Ws) - v_start = sqrt(v_end^2 - 2*a_braking *s_braking) # braking start velocity (in m/s) -# return floor(v_start, digits=approxLevel) - return floor(v_start, digits=approxLevel +1) -end #function calcBrakingStartVelocity - -function calcBrakingAcceleration(v_start::Real, v_end::Real, s_braking::Real) - # equation is based on [Wende:2003, page 37] - - # v_start: braking start velocity (in m/s) - # v_end: target velocity at the end of braking (in m/s) - # s_braking: braking distance (in Ws) - a_braking = (v_end^2 - v_start^2) /2 /s_braking # constant braking acceleration (in m/s^2) - return a_braking -end #function calcBrakingAcceleration diff --git a/src/Import.jl b/src/Import.jl deleted file mode 100644 index aae95af..0000000 --- a/src/Import.jl +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -""" -Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. -""" -function importYamlFiles(trainDirectory::String, pathDirectory::String) - train = importFromYaml(:train, trainDirectory) - path = importFromYaml(:path, pathDirectory) - - return (train, path) -end #function importYamlFiles - - """ - Read the train information from a YAML file, save it in a Dict and return it. - """ -function importFromYaml(dataType::Symbol, directory::String) - dataSet = String(dataType) - data = YAML.load(open(directory)) - if collect(keys(data))[1] != dataSet - error("ERROR at reading the ", dataSet, " yaml file: The data set is called ", collect(keys(data))[1]," and not ", dataSet, ".") - end - dataKeys = collect(keys(data[dataSet])) - dataKeys = collect(keys(data[dataSet])) - dataValues = collect(values(data[dataSet])) - dictionary = Dict() - for number in 1:length(dataKeys) - merge!(dictionary, Dict(Symbol(dataKeys[number]) => dataValues[number])) - end - return dictionary -end # function importFromYaml diff --git a/src/Output.jl b/src/Output.jl deleted file mode 100644 index a21c12e..0000000 --- a/src/Output.jl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg" -# __copyright__ = "2020-2022" -# __license__ = "ISC" - -function createOutput(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) - if settings.outputDetail == :running_time - output = movingSection[:t] # TODO: or use drivingCourse[end][:t] - - elseif settings.outputDetail == :points_of_interest - # add points of interest - if haskey(path, :pointsOfInterest) - output = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(output, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - end - - elseif settings.outputDetail == :driving_course - output = drivingCourse - - elseif settings.outputDetail == :everything - output = Dict{Symbol,Any}() - merge!(output, Dict(:train => train, :path => path, :settings => settings)) - - - # add moving section and driving courses - if settings[:operationModeMinimumRunningTime] == true - merge!(output, Dict(:movingSectionMinimumRunningTime => movingSection, - :drivingCourseMinimumRunningTime => drivingCourse)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(output, Dict(:movingSectionMinimumEnergyConsumption => movingSection, - :drivingCourseMinimumEnergyConsumption => drivingCourse)) - end - - # add points of interest - if haskey(path, :pointsOfInterest) - pointsOfInterest = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(pointsOfInterest, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - - if settings[:operationModeMinimumRunningTime] == true - merge!(output, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(output, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) - end - end - else - output = nothing - end - return output -end - -#= -function createOutputDict(train::Dict, settings::Settings, path::Dict, movingSection::Dict, drivingCourse::Vector{Dict}) - outputDict = Dict{Symbol,Any}() - merge!(outputDict, Dict(:train => train, :path => path, :settings => settings)) - - - # add moving section and driving courses - if settings[:operationModeMinimumRunningTime] == true - merge!(outputDict, Dict(:movingSectionMinimumRunningTime => movingSection, - :drivingCourseMinimumRunningTime => drivingCourse)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(outputDict, Dict(:movingSectionMinimumEnergyConsumption => movingSection, - :drivingCourseMinimumEnergyConsumption => drivingCourse)) - end - - # add points of interest - if haskey(path, :pointsOfInterest) - pointsOfInterest = Vector{Dict}() - POI = 1 - i = 1 - while POI <= length(path[:pointsOfInterest]) && i <= drivingCourse[end][:i] - if path[:pointsOfInterest][POI] == drivingCourse[i][:s] - push!(pointsOfInterest, drivingCourse[i]) - POI = POI+1 - end - i = i+1 - end - - if settings[:operationModeMinimumRunningTime] == true - merge!(outputDict, Dict(:pointsOfInterestMinimumRunningTime => pointsOfInterest)) - elseif settings[:operationModeMinimumEnergyConsumption] == true - merge!(outputDict, Dict(:pointsOfInterestMinimumEnergyConsumption => pointsOfInterest)) - end - end - - return outputDict -end # function createOutputDict -=# diff --git a/src/Types.jl b/src/Types.jl deleted file mode 100644 index 3419b44..0000000 --- a/src/Types.jl +++ /dev/null @@ -1,799 +0,0 @@ -#!/usr/bin/env julia -# -*- coding: UTF-8 -*- -# __julia-version__ = 1.7.2 -# __author__ = "Max Kannenberg, Martin Scheidt" -# __copyright__ = "2022" -# __license__ = "ISC" - -""" - Settings(file) - -Settings is a datastruture for calculation context. -The function Settings() will create a set of settings for the train run calculation. -`file` is optinal may be used to load settings in the YAML format. - -# Example -```jldoctest -julia> my_settings = Settings() # will generate default settings -Settings(mass_point, :distance, 20, 3, running_time, julia_dict, ".") -``` -""" -struct Settings - - massModel::Symbol # model type of train mass ":mass_point" or ":homogeneous_strip". - stepVariable::Symbol # variable of the linear multistep method: ":distance", ":time" or ":velocity". - stepSize::Real # step size, unit depends on stepVariable - :distance in meter, time in seconds and velocity in meter/second. - approxLevel::Int # value for approximation; used when rounding or interating. - outputDetail::Symbol # single Float() ":running_time", Array() of ":points_of_interest", - # complete Array() ":driving_course", or Dict() ":everything". - outputFormat::Symbol # output as ":julia_dict" or as ":csv". - outputDir::String # if outputFormat is not ":julia_dict". - - ## constructor - function Settings(file="DEFAULT") - - ## default values - massModel = :mass_point - stepVariable = :distance - stepSize = 20 - approxLevel = 3 - outputDetail = :running_time - outputFormat = :julia_dict - outputDir = "." - - if file != "DEFAULT" - ## JSON schema for YAML-file validation - schema = Schema("""{ - "properties": { - "massModel": { - "description": "type of train model", - "type": "string", - "enum": [ "mass_point", "homogeneous_strip" ] - }, - "stepVariable": { - "description": "variable of the linear multistep method", - "type": "string", - "enum": [ "distance", "time", "velocity" ] - }, - "stepSize": { - "description": "step size acording to stepVariable", - "type": "number", - "exclusiveMinimum": 0 - }, - "outputDetail": { - "description": "Selecting the detail of the result", - "type": "string", - "enum": [ "running_time", "points_of_interest", "driving_course", "everything" ] - }, - "outputFormat": { - "description": "Output format", - "type": "string", - "enum": [ "julia_dict", "csv" ] - }, - "outputDir": { - "description": "Path for the CSV export", - "type": "string" - } - } - }""") - - settings = YAML.load(open(file))["settings"] - - ## validate the loaded file - try - validate(schema, settings) - catch err - println("Could not load settings file $file.\n Format is not recognized - using default as fall back.") - settings = Dict() - end - - ## set the variables if they exist in "settings" - haskey(settings, "massModel") ? massModel = Symbol(settings["massModel"]) : nothing - haskey(settings, "stepVariable") ? stepVariable = Symbol(settings["stepVariable"]) : nothing - haskey(settings, "stepSize") ? stepSize = settings["stepSize"] : nothing - haskey(settings, "approxLevel") ? approxLevel = settings["approxLevel"] : nothing - haskey(settings, "outputDetail") ? outputDetail = Symbol(settings["outputDetail"]) : nothing - haskey(settings, "outputFormat") ? outputFormat = Symbol(settings["outputFormat"]) : nothing - haskey(settings, "outputDir") ? outputDir = settings["outputDir"] : nothing - end - - new(massModel, stepVariable, stepSize, approxLevel, outputDetail, outputFormat, outputDir) - - end #function Settings() # constructor - -end #struct Settings - -""" -Read the input information from YAML files for train, path and settings, save it in different dictionaries and return them. -""" -function checkAndSetInput!(train::Dict, path::Dict, settings::Settings) - checkAndSetTrain!(train) - checkAndSetPath!(path) - - if settings.outputDetail == :points_of_interest && !haskey(path, :pointsOfInterest) - throw(DomainError(settings.outputDetail, "INFO at checking the input for settings and path:\n - settings[:outputDetail] is 'points of interest' but the path does not for pointsOfInterest.")) - end - return (train, path) -end #function checkAndSetInput! - -""" -Read the train information from a YAML file, save it in a train Dict and return it. -""" -function checkAndSetTrain!(train::Dict) - # check train information from input dictionary - - checkAndSetString!(train, "train", :name, "") # train's name - # add train's identifier if not existing - if !(haskey(train, :id) && train[:id]!=nothing) - merge!(train, Dict(:id =>1)) - end - checkAndSetString!(train, "train", :type, "passenger", ["passenger", "freight"]) # train type "passenger" or "freight" - - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :length, :l_train, "m", 20.0) # total length (in m) - # TODO: or just use: checkAndSetPositiveNumber!(train, "train", :length, "m", 20.0) - - checkAndSetSpeedLimit!(train) # train's speed limit (in m/s) - checkAndSetBrakingAcceleration!(train) # a_braking - - checkAndSetPositiveNumber!(train, "train", :m_td, "kg", 80000) # mass on the traction unit's driving axles (in kg) - checkAndSetPositiveNumber!(train, "train", :m_tc, "kg", 0.0) # mass on the traction unit's carrying axles (in kg) - checkAndSetSum!(train, "train", :m_t, :m_td, :m_tc) # mass of the traction unit (in kg) - - checkAndSetPositiveNumber!(train, "train", :m_w, "kg", 0.0) # mass of the set of wagons (consist) (in kg) - checkAndSetSum!(train, "train", :m_train, :m_t, :m_w) # total mass (in kg) - if train[:m_train] <= 0.0 - error("ERROR at checking the input for the train: The train's mass has to be higher than 0.0 kg.") - end - - checkAndSetRotationMassFactors!(train) - checkAndSetTractiveEffortVelocityPairs!(train) # pairs of velocity and tractive effort - - # coefficients for the vehicle resistance of the traction unit - checkAndSetRealNumber!(train, "train", :Δv_t, "m/s", 15.0/3.6) # coefficient for velocitiy difference between traction unit and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rtd0, "‰", 0.0) # coefficient for basic resistance due to the traction units driving axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rtc0, "‰", 0.0) # coefficient for basic resistance due to the traction units carring axles (in ‰) - checkAndSetPositiveNumber!(train, "train", :F_Rt2, "N", 0.0) # coefficient for air resistance of the traction units (in N) - - # coefficients for the vehicle resistance of the set of wagons (consist) - checkAndSetRealNumber!(train, "train", :Δv_w, "m/s", getDefault_Δv_w(train[:type])) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - checkAndSetPositiveNumber!(train, "train", :f_Rw0, "‰", 0.0) # coefficient for basic resistance of the set of wagons (consist) (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw1, "‰", 0.0) # coefficient for the consists resistance to rolling (in ‰) - checkAndSetPositiveNumber!(train, "train", :f_Rw2, "‰", 0.0) # coefficient fo the consistsr air resistance (in ‰) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, :id, :type, - :length, :l_train, :v_limit, :v_limit_kmh, :a_braking, - :m_train, :m_t, :m_td, :m_tc, :m_w, - :ξ_train, :ξ_t, :ξ_w, :rotationMassFactor_train, :rotationMassFactor_t, :rotationMassFactor_w, - :tractiveEffortVelocityPairs, :F_T_pairs, :F_T_pairs_kmh, - :f_Rtd0, :f_Rtc0, :F_Rt2, :Δv_t, - :f_Rw0, :f_Rw1, :f_Rw2, :Δv_w] - informAboutUnusedKeys(collect(keys(train)), usedKeys::Vector{Symbol}, "train") - - return train -end #function checkAndSetTrain! - -function checkAndSetPath!(path::Dict) - # check path information from input dictionary - - checkAndSetString!(path, "path", :name, "") - # TODO checkId ? path[:id] # path identifier - checkAndSetSections!(path) - checkAndSetPOIs!(path) - - # inform the user about keys of the input dictionary that are not used in this tool - usedKeys = [:name, - :sections, :sectionStarts, :sectionStarts_kmh, - :pointsOfInterest] - informAboutUnusedKeys(collect(keys(path)), usedKeys::Vector{Symbol}, "path") - - return path -end # function checkAndSetPath! - -function checkAndSetBool!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::Bool) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) != Bool - error("ERROR at checking the input for the ",dictionaryType,": The value of the key ",String(key)," is not correct. The value has to be of type Bool.") - end - else - merge!(dictionary, Dict(key => defaultValue)) - defaultValue && println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," or its value is missing. Therefore ",String(key),"=",dictionary[key]," is assumed and used.") - end - return dictionary -end #function checkAndSetBool! - -function checkAndSetPositiveNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real && dictionary[key] >= 0.0 - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetPositiveNumber! - -# first method without a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # do nothing - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -# second method with a default value -function checkAndSetPositiveNumberWithDifferentNames!(dictionary::Dict, dictionaryType::String, mainKey::Symbol, alternativeKey::Symbol, unit::String, default::Real) - mainKey_temp = -1.0 - alternativeKey_temp = -1.0 - - if haskey(dictionary, mainKey) && dictionary[mainKey]!=nothing - if typeof(dictionary[mainKey]) <: Real && dictionary[mainKey] >= 0.0 - mainKey_temp = dictionary[mainKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",mainKey," is no real floating point number >=0.0.") - end - end - - if haskey(dictionary, alternativeKey) && dictionary[alternativeKey]!=nothing - if typeof(dictionary[alternativeKey]) <: Real && dictionary[alternativeKey] >= 0.0 - alternativeKey_temp = dictionary[alternativeKey] - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",alternativeKey," is no real floating point number >=0.0.") - end - else - delete!(dictionary, alternativeKey) - end - - if mainKey_temp >= 0.0 && alternativeKey_temp >= 0.0 - difference = abs(mainKey_temp - alternativeKey_temp) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(dictionary, alternativeKey) - println("WARNING at checking the input for the ",dictionaryType,": The values of ",mainKey," and ",alternativeKey," differ by ",difference," ",unit,". The value ",String(mainKey),"=",default," ",unit," is used." ) - end - elseif mainKey_temp >= 0.0 - # do nothing - elseif alternativeKey_temp >= 0.0 - merge!(dictionary, Dict(mainKey => alternativeKey_temp)) - else - # set a default value - merge!(dictionary, Dict(mainKey, default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",mainKey," or its value is missing. Therefore the value ",String(mainKey),"=",default," ",unit," is used." ) - end - - return dictionary -end #function checkAndSetPositiveNumberWithDifferentNames! - -function checkAndSetRealNumber!(dictionary::Dict, dictionaryType::String, key::Symbol, unit::String, default::Real) - if haskey(dictionary,key) && dictionary[key]!=nothing - if typeof(dictionary[key]) <: Real - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is no real number.") - end - else - merge!(dictionary, Dict(key => default)) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. Therefore ",String(key),"=",default," ",unit," will be assumed and used." ) - end - - return dictionary -end #function checkAndSetRealNumber! - -function checkAndSetSum!(dictionary::Dict, dictionaryType::String, sum::Symbol, summand1::Symbol, summand2::Symbol) - if haskey(dictionary,sum) && dictionary[sum]!=nothing - if typeof(dictionary[sum]) <: Real && dictionary[sum] >= 0.0 - difference = abs(dictionary[sum] - (dictionary[summand1]+dictionary[summand2])) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is not exactly the sum of ",String(summand1)," and ",String(summand2),". It differs by ",difference,".") - end - else - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(sum)," is no real floating point number >=0.0.") - end - else - merge!(dictionary, Dict(sum => dictionary[summand1]+dictionary[summand2])) - println("INFO at checking the input for the ",dictionaryType,": The key ",String(sum)," is missing. Therefore ",String(sum)," = ",String(summand1)," + ",String(summand2)," = ",dictionary[sum]," was calculated and will be used." ) - end - - return dictionary -end #function checkAndSetSum! - -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String, validValues::Vector{String}) - # TODO change checkAndAddString! to checkAndAddSymbol! ? - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - for validValue in validValues - if value == validValue - return dictionary - end - end - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be one of the following String values: ", validValues) - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. It has to be one of the following String values: ", validValues,". For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! -# second method of function checkAndSetString! without validValues -function checkAndSetString!(dictionary::Dict, dictionaryType::String, key::Symbol, defaultValue::String) - if haskey(dictionary,key) && dictionary[key]!=nothing - value = dictionary[key] - if typeof(value) == String - return dictionary - end - error("ERROR at checking the input for the ",dictionaryType,": The value of ",String(key)," is wrong. It has to be of type String.") - else - println("INFO at checking the input for the ",dictionaryType,": The key ",String(key)," is missing. For this calculation the default value '",defaultValue,"' will be used.") - merge!(dictionary, Dict(key => defaultValue)) - end - return dictionary -end #function checkAndSetString! - -function checkAndSetSpeedLimit!(train::Dict) - v_limit_temp = 0.0 - v_limit_kmh_temp = 0.0 - - if haskey(train, :v_limit) && train[:v_limit]!=nothing - if typeof(train[:v_limit]) <: Real && train[:v_limit] >= 0.0 - v_limit_temp = train[:v_limit] - else - error("ERROR at checking the input for the train: The value of v_limit is no real floating point number >=0.0.") - end - end - - if haskey(train, :v_limit_kmh) && train[:v_limit_kmh]!=nothing - if typeof(train[:v_limit_kmh]) <: Real && train[:v_limit_kmh] >= 0.0 - v_limit_kmh_temp = train[:v_limit_kmh] - else - error("ERROR at checking the input for the train: The value of v_limit_kmh is no real floating point number >=0.0.") - end - else - delete!(train, :v_limit_kmh) - end - - if v_limit_temp > 0.0 && v_limit_kmh_temp > 0.0 - difference = abs(v_limit_temp - v_limit_kmh_temp/3.6) - if difference > 1/(10^approxLevel) # TODO or use difference > 0.0 ? - delete!(train, :v_limit_kmh) - println("WARNING at checking the input for the train: The values of v_limit and v_limit_kmh differ by ",difference," m/s. The value v_limit=",v_limit_temp," m/s is used." ) - end - elseif v_limit_temp > 0.0 - # do nothing - elseif v_limit_kmh_temp > 0.0 - merge!(train, Dict(:v_limit => v_limit_kmh_temp/3.6)) - else - # set a default value - merge!(train, Dict(:v_limit, 1000.0/3.6)) # set speed limit to 1000 km/h - println("INFO at checking the input for the train: There is no value for the trains speed limit (v_limit or v_limit_kmh). The value v_limit=1000 km/h =",train[:v_limit]," m/s is used." ) - end - - return train -end #function checkAndSetSpeedLimit! - -function checkAndSetBrakingAcceleration!(train::Dict) - if haskey(train, :a_braking) && train[:a_braking]!=nothing - if typeof(train[:a_braking]) <: Real - if train[:a_braking] > 0.0 - train[:a_braking] =-train[:a_braking] - println("INFO at checking the input for the train: The value for a_braking is >0.0. The braking acceleration has to be <0.0. Therefore a_braking=",train[:a_braking]," m/s^2 is used." ) - elseif train[:a_braking] == 0.0 - error("ERROR at checking the input for the train: The value for a_braking is 0.0. The braking acceleration has to be <0.0.") - end - else - error("ERROR at checking the input for the train: The value for a_braking is no real floating point number <0.0.") - end - else - # set a default value depending on the train type - if train[:type] == "freight" - a_braking = -0.225 - elseif train[:type] == "passenger" - a_braking = -0.375 - #elseif train[:type] == "passengerSuburban" - # a_braking = -0.525 - # TODO: add suburban trains to train type? - end - - merge!(train, Dict(:a_braking => a_braking)) - println("INFO at checking the input for the train: The key for a_braking is missing. Because of the train type ",train[:type]," a_braking=",a_braking," m/s^2 will be assumed and used." ) - end - - return train -end #function checkAndSetBrakingAcceleration! - -function checkAndSetRotationMassFactors!(train::Dict) - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_train, :rotationMassFactor_train, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_t, :rotationMassFactor_t, "") - checkAndSetPositiveNumberWithDifferentNames!(train, "train", :ξ_w, :rotationMassFactor_w, "") - if haskey(train, :ξ_train) && train[:ξ_train]!=nothing - if train[:ξ_train]>0.0 - if haskey(train, :ξ_t) && train[:ξ_t]!=nothing && train[:ξ_t]>0.0 && (train[:m_w]==0.0 || (haskey(train, :ξ_w) && train[:ξ_w]!=nothing)) - # TODO: is && train[:ξ_t]>0.0 necessary here? - difference = abs(train[:ξ_train] - (train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train]) - if difference > 1/(10^approxLevel) - error("ERROR at checking the input for the train: The value of ξ_train is not exactly ξ_train=(ξ_t*m_t + ξ_w*m_w)/m_train. It differs by ",difference,".") - end - end - else - error("ERROR at checking the input for the train: The value of :ξ_train is no real floating point number >0.0.") - end - else - checkAndSetPositiveNumber!(train, "train", :ξ_t, "", 1.09) - - if train[:m_w]>0.0 - default_ξ_w = 1.06 - else - default_ξ_w = 0.0 - end - checkAndSetPositiveNumber!(train, "train", :ξ_w, "", default_ξ_w) - - - ξ_train=(train[:ξ_t]*train[:m_t] + train[:ξ_w]*train[:m_w])/train[:m_train] # rotation mass factor of the whole train (without unit) - if ξ_train <= 0.0 - error("ERROR at checking the input for the train: The train's rotations mass factor has to be higher than 0.0 kg.") - end - merge!(train, Dict(:ξ_train => ξ_train)) - end - - return train -end #function checkAndSetRotationMassFactors! - -function checkAndSetTractiveEffortVelocityPairs!(train::Dict) # pairs of velocity and tractive effort - if haskey(train,:tractiveEffortVelocityPairs) && train[:tractiveEffortVelocityPairs]!=nothing - pairs = train[:tractiveEffortVelocityPairs] - velocityMultiplier = 1.0 - - if (haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing) && (haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing) - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs, F_T_pairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs. The values for tractiveEffortVelocityPairs are used." ) - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for tractiveEffortVelocityPairs and F_T_pairs_kmh. The values for tractiveEffortVelocityPairs are used." ) - end - - elseif haskey(train,:F_T_pairs) && train[:F_T_pairs]!=nothing - pairs = train[:F_T_pairs] - velocityMultiplier = 1.0 - - if haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - println("WARNING at checking the input for the train: There are values for F_T_pairs and F_T_pairs_kmh. The values for F_T_pairs are used." ) - end - - elseif haskey(train,:F_T_pairs_kmh) && train[:F_T_pairs_kmh]!=nothing - velocityMultiplier = 1000/3600 - pairs=[] - for row in 1:length(train[:F_T_pairs_kmh]) - push!(pairs, [train[:F_T_pairs_kmh][row][1]*velocityMultiplier, train[:F_T_pairs_kmh][row][2]]) - end # for - - else - error("ERROR at checking the input for the train: There has to be the key tractiveEffortVelocityPairs filled with a list of pairs of velocity and tractive effort.") - end # if - - # check if the elements of the array have the correct type - errorDetected=false - - for row in 1:length(pairs) - if typeof(pairs[row][1]) <: Real && pairs[row][1]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - if typeof(pairs[row][2]) <: Real && pairs[row][2]>=0.0 - else - errorDetected=true - println("ERROR at checking the input for the train: The tractive effort value of train[:tractiveEffortVelocityPairs] in row ", row ," is no real floating point number >=0.0.") - end - - if row>=2 && pairs[row][1] <= pairs[row-1][1] - errorDetected=true - println("ERROR at checking the input for the train: The speed value of train[:tractiveEffortVelocityPairs] in row ", row ," (v=",pairs[row][1]," m/s) is not higher than the speed value in the previous row (v=",pairs[row-1][1]," m/s).") - end - end # for - if errorDetected - error("ERROR at checking the input for the train: Only real floating point number >=0.0 are allowed for speed and tractive effort. The speed values have to be listed from low to high.") - end - - # create tractiveEffortVelocityPairs - if pairs[1][1]>0.0 # if there is no F_T for v=0.0, the first known value is used - newPairs=[] - push!(newPairs, [0.0, pairs[1][2]]) - println("INFO at checking the input for the train: The tractive effort for v=0.0 m/s is missing. Therefore the first given value F_T(v=",pairs[1][1]," m/s)=",pairs[1][2]," N will be used." ) - for row in 1:length(pairs) - push!(newPairs, [pairs[row][1], pairs[row][2]]) - end # for - merge!(train, Dict(:tractiveEffortVelocityPairs => newPairs)) - else - merge!(train, Dict(:tractiveEffortVelocityPairs => pairs)) - end - - if length(pairs[1])>2 - println("INFO according the train dictionary: Only the first two columns of train[:tractiveEffortVelocityPairs] are used in this tool.") - end - - return train -end #function checkAndSetTractiveEffortVelocityPairs! - -function getDefault_Δv_w(type::String) # coefficient for velocitiy difference between set of wagons (consist) and outdoor air (in m/s) - if type == "passenger" - # TODO if different passenger or freight trains are posiible, use: if startswith(type, "passenger"). exanples: passengerLocomotivehauled and passengerMotorCoachTrain - Δv_w=15.0/3.6 - elseif type == "freight" - Δv_w=0.0 - end # if - - return Δv_w -end #function getDefault_Δv_w! - -function checkAndSetSections!(path::Dict) - # check the section information - if haskey(path,:sections) && path[:sections]!=nothing - # TODO: check typeof(path[:sections]) == Dict - if (haskey(path, :sectionStarts) && path[:sectionStarts]!=nothing) && (haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing) - println("WARNING at checking the input for the path: There are values for sections, sectionStarts and sectionStarts_kmh. The dictionary sections is used." ) - - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts. The dictionary sections is used." ) - - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sections and sectionStarts_kmh. The dictionary sections is used." ) - end - elseif haskey(path,:sectionStarts) && path[:sectionStarts]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts) - - if haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The array sectionStarts is used." ) - end - elseif haskey(path,:sectionStarts_kmh) && path[:sectionStarts_kmh]!=nothing - # TODO: check typeof(path[:sections]) == Array - createSections!(path, :sectionStarts_kmh) - else - error("ERROR at checking the input for the path: The Symbol :sections is missing. It has to be added with a list of sections. Each has to be a dictionary with the keys :s_tart, :s_end, :v_limit and :f_Rp.") - section = Dict(:s_start => 0.0, - :s_end => 15.0, - :v_limit => 1000.0/3.6, - :f_Rp => 0.0) - merge!(path, Dict(:sections => [section])) - return path - end - - sections = path[:sections] - - checkedSections = [] - increasing = false - decreasing = false - - #TODO: throw error for each issue or collect the issues and use the Bool errorDetected like in createSections? - - # check values for section==1 - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_start, "m", 0.0) # first point of the section (in m) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :s_end, "m", 0.0) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[1], "path[:sections][1]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[1], "path[:sections][1]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - - push!(checkedSections, sections[1]) - - if sections[1][:s_start] < sections[1][:s_end] - increasing = true - elseif sections[1][:s_start] > sections[1][:s_end] - decreasing = true - else - pop!(checkedSections) - println("WARNING at checking the input for the path: The first section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end - - - for sectionNr in 2:length(sections) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_start, "m", sections[sectionNr-1][:s_end]) # first point of the section (in m) - # TODO how to define default values? which has to be checked and defined fist? s_end-1 and s_start need each other as default values - #if sectionNr < length(sections) && haskey(sections[sectionNr], :s_start) && sections[sectionNr][:s_start]!=nothing && typeof(sections[sectionNr][:s_start]) <: Real - # defaultEnd = sections[sectionNr+1][:s_start] - #end - defaultEnd = sections[sectionNr][:s_start] # so the default value for s_end creates a sections of lenght=0.0 #TODO should be changed! - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :s_end, "m", defaultEnd) # first point of the next section (in m) - checkAndSetPositiveNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :v_limit, "m/s", 1000.0/3.6) # paths speed limt (in m/s) - checkAndSetRealNumber!(sections[sectionNr], "path[:sections]["*string(sectionNr)*"]", :f_Rp, "‰", 0.0) # specific path resistance of the section (in ‰) - - push!(checkedSections, sections[sectionNr]) - - # compare the section's start and end position - if sections[sectionNr][:s_start] < sections[sectionNr][:s_end] - increasing = true - elseif sections[sectionNr][:s_start] > sections[sectionNr][:s_end] - decreasing = true - else - pop!(checkedSections) - println("INFO at checking the input for the path: The ",sectionNr,". section of :sections has the same position for starting and end point. The section will be deleted and not used in the tool.") - end - if increasing && decreasing - error("ERROR at checking the input for the path: The positions of the :sections are not increasing/decreasing consistently. The direction in the ",sectionNr,". section differs from the previous.") - end - - - if length(checkedSections)>1 && sections[sectionNr][:s_start] != checkedSections[end-1][:s_end] - error("ERROR at checking the input for the path[:sections]: The starting position of the ",section,". section (s=",sections[sectionNr][:s_start]," m) does not euqal the last position of the previous section(s=",checkedSections[end-1][:s_end]," m). The sections have to be sequential.") - # TODO: maybe if there is a gab create a new section and only if there a jumps in the wrong direction throw an error? - end - end #for - - return path -end #function checkAndSetSections! - -function createSections!(path::Dict, key::Symbol) - # read the section starting positions and corresponding information - if key == :sectionStarts - sectionStartsArray = path[:sectionStarts] - conversionFactor = 1.0 # conversion factor between the units m/s and m/s - - if haskey(path,:sectionStarts) && path[:sectionStarts_kmh]!=nothing - println("WARNING at checking the input for the path: There are values for sectionStarts and sectionStarts_kmh. The values for sectionStarts are used." ) - end - elseif key == :sectionStarts_kmh - sectionStartsArray = path[:sectionStarts_kmh] - conversionFactor = 1/3.6 # conversion factor between the units km/h and m/s - else - error("ERROR at checking the input for the path: The keyword sectionStarts or sectionStarts_kmh is missing. The sections can not be created without them.") - end # if - - # check if the array is correct and if elements of the array have the correct type and valid values - errorDetected = false - if length(sectionStartsArray)<2 - error("ERROR at checking the input for the path: The keyword ",key," needs at least two rows for two points each with the three columns [s, v_limit, f_Rp].") - end - - for row in 1:length(sectionStartsArray) - if length(sectionStartsArray[row])>=3 - if length(sectionStartsArray[row])>3 - println("INFO at checking the input for the path: Only the first three columns of sectionStartsArray are used in this tool.") - end - else - error("ERROR at checking the input for the path: The keyword ",key," needs to be filled with the three columns [s, v_limit, f_Rp].") - end - - if !(typeof(sectionStartsArray[row][1]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The position value (column 1) of ",key," in row ", row ," is no real floating point number.") - end - if !(typeof(sectionStartsArray[row][2]) <: Real && sectionStartsArray[row][2] >= 0.0) - errorDetected=true - println("ERROR at checking the input for the path: The speed limit (column 2) of ",key," in row ", row ," is no real floating point number >=0.0.") - end - if !(typeof(sectionStartsArray[row][3]) <: Real) - errorDetected=true - println("ERROR at checking the input for the path: The tractive effort value (column 3) of ",key," in row ", row ," is no real floating point number.") - end - end # for - if errorDetected - error("ERROR at checking the input for the path: The values of ",key," have to be corrected.") - end - - - sections = [] - for row in 2:length(sectionStartsArray) - s_start = sectionStartsArray[row-1][1] # first point of the section (in m) - s_end = sectionStartsArray[row][1] # first point of the next section (in m) - v_limit = sectionStartsArray[row-1][2]*conversionFactor # paths speed limt (in m/s) - f_Rp = sectionStartsArray[row-1][3] # specific path resistance of the section (in ‰) - - section = Dict(:s_start => s_start, - :s_end => s_end, - :v_limit => v_limit, - :f_Rp => f_Rp) - push!(sections, section) - end # for - # s_start in first entry defines the path's beginning - # s_end in last entry defines the path's ending - - merge!(path, Dict(:sections => sections)) - return path -end #function createSections! - -function checkAndSetPOIs!(path::Dict) - # read the section starting positions and corresponding information - if haskey(path, :pointsOfInterest) - if path[:pointsOfInterest] != nothing - pointsOfInterest = path[:pointsOfInterest] - - sortingNeeded = false - errorDetected = false - for element in 1:length(pointsOfInterest) - if typeof(pointsOfInterest[element]) <: Real - if element > 1 - if pointsOfInterest[element] < pointsOfInterest[element-1] - sortingNeeded = true - println("INFO at checking the input for the path: The point of interest in element ", element ," (",pointsOfInterest[element]," m) has to be higher than the value of the previous element (",pointsOfInterest[element-1]," m). The points of interest will be sorted.") - end - end - else - errorDetected = true - println("ERROR at checking the input for the path: The point of interest in element ", element ," is no real floating point number.") - end - end # for - - if errorDetected - error("ERROR at checking the input for the path: The values of pointsOfInterest have to be corrected.") - end - if sortingNeeded == true - sort!(pointsOfInterest) - end - - copiedPOIs = [] - for element in 1:length(pointsOfInterest) - if element == 1 - push!(copiedPOIs, pointsOfInterest[element]) - elseif element > 1 && pointsOfInterest[element] > pointsOfInterest[element-1] - push!(copiedPOIs, pointsOfInterest[element]) - end - end # for - path[:pointsOfInterest] = copiedPOIs - - else - println("INFO at checking the input for the path: The key pointsOfInterest exists but without values.") - delete!(path, :pointsOfInterest) - end - end - - return path -end #function checkAndSetPOIs! - -#function informAboutUnusedKeys(dictionary::Dict, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool -function informAboutUnusedKeys(allKeys::AbstractVector, usedKeys::Vector{Symbol}, dictionaryType::String) # inform the user which Symbols of the input dictionary are not used in this tool - unusedKeys = [] - # find unused keys in allKeys - for key in allKeys - used = false - for usedKey in usedKeys - if key == usedKey - used = true - break - end - end - if !used - push!(unusedKeys, key) - end - end - - if length(unusedKeys)>0 - println("INFO at checking the input for the ",dictionaryType,": The following Keywords are not used in this tool:") - for key in unusedKeys - println(" - ",key) - end - end -end #function informAboutUnusedKeys