From fab1ad017ab12cc3d82021ecb775f2a7e65ff107 Mon Sep 17 00:00:00 2001 From: Martin Scheidt Date: Fri, 3 Jun 2022 17:24:16 +0200 Subject: [PATCH] refactored functions, removed characteristics.jl, and unified naming in constructors.jl --- CHANGELOG.md | 16 +- src/TrainRuns.jl | 1 - src/behavior.jl | 352 +++++++++++++++++++---------------------- src/calc.jl | 195 ++++++++++++++++++++++- src/characteristics.jl | 170 -------------------- src/constructors.jl | 54 +++---- 6 files changed, 395 insertions(+), 393 deletions(-) delete mode 100644 src/characteristics.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ccca20..64524be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,10 +23,18 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * renamed Validate.jl into types.jl * renamed TrainRunCalc.jl into calc.jl * moved trainrun function from calc.jl to TrainRun.jl -* moved createDataPoint() from behavior.jl to types.jl -* moved createBehaviorSection() from behavior.jl to types.jl -* moved createMovingSection() from characteristics.jl to types.jl -* moved createCharacteristicSection() from characteristics.jl to types.jl +* moved createDataPoint() from behavior.jl to constructors.jl +* moved createBehaviorSection() from behavior.jl to constructors.jl +* moved createMovingSection() from characteristics.jl to constructors.jl +* moved createCharacteristicSection() from characteristics.jl to constructors.jl +* removed characteristics.jl and moved all functions inside to behavior.jl +* moved calculateTractiveEffort() from behavior.jl to calc.jl +* moved calculatePathResistance() from behavior.jl to calc.jl +* moved calculateForces!() from behavior.jl to calc.jl +* moved moveAStep() from behavior.jl to calc.jl +* moved getCurrentSpeedLimit() from behavior.jl to calc.jl +* moved getNextPointOfInterest() from behavior.jl to calc.jl +* moved determineCharacteristics() from behavior.jl to calc.jl * changed title of include files from upper case to lower case * changed seperation of submodules into a single module with file include * updated test files to railtoolkit/schema (2022.05) diff --git a/src/TrainRuns.jl b/src/TrainRuns.jl index d6de6ad..1972ef6 100644 --- a/src/TrainRuns.jl +++ b/src/TrainRuns.jl @@ -27,7 +27,6 @@ include("types.jl") include("constructors.jl") include("formulary.jl") include("calc.jl") -include("characteristics.jl") include("behavior.jl") include("output.jl") diff --git a/src/behavior.jl b/src/behavior.jl index 7ddae8c..a4f5667 100644 --- a/src/behavior.jl +++ b/src/behavior.jl @@ -5,188 +5,6 @@ # __copyright__ = "2020-2022" # __license__ = "ISC" -""" - 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{}`: 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::Array{}) - 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::Train) - - if massModel == :mass_point - pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full) - elseif massModel == :homogeneous_strip - pathResistance = 0.0 - s_rear = s - train.length # position of the rear of the train - 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_full) - csId = csId-1 - if csId == 0 - # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used - return pathResistance + (CSs[1][:s_entry] - s_rear) / train.length * calcForceFromCoefficient(CSs[1][:r_path], train.m_train_full) - 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::Train, 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.tractiveEffort)) - else # bsType == "accelerating" || bsType == "diminishing" || 'default' - dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort) - 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{Tuple}, s::Real) - for s_POI in pointsOfInterest - if s_POI[1] > 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 @@ -196,7 +14,7 @@ function addBreakFreeSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags: trainIsHalting = drivingCourse[end][:v] == 0.0 if trainIsHalting && !endOfCSReached - BS = createBehaviorSection("breakFree", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("breakFree", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) drivingCourse[end][:behavior] = BS[:type] # traction effort and resisting forces (in N) @@ -318,7 +136,7 @@ function addAcceleratingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFla # use the conditions for the accelerating section if !targetSpeedReached && !endOfCSReached && tractionSurplus && !brakingStartReached - BS = createBehaviorSection("accelerating", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("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) @@ -568,7 +386,7 @@ function addCruisingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: 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]) + BS = BehaviorSection(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]) @@ -815,7 +633,7 @@ function addDiminishingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlag # use the conditions for the diminishing section if tractionDeficit && !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("diminishing", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("diminishing", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) drivingCourse[end][:behavior] = BS[:type] while tractionDeficit && !targetSpeedReached && !endOfCSReached && !brakingStartReached @@ -995,7 +813,7 @@ function addCoastingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags:: # use the conditions for the coasting section if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("coasting", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("coasting", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) drivingCourse[end][:behavior] = BS[:type] while !targetSpeedReached && !endOfCSReached && !brakingStartReached @@ -1152,7 +970,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D # use the conditions for the braking section if !targetSpeedReached && !endOfCSReached - BS = createBehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("braking", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) drivingCourse[end][:behavior] = BS[:type] while !targetSpeedReached && !endOfCSReached @@ -1172,7 +990,7 @@ function addBrakingSection!(CS::Dict, drivingCourse::Vector{Dict}, stateFlags::D 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()) + push!(drivingCourse, DataPoint()) drivingCourse[end][:i] = drivingCourse[end-1][:i]+1 drivingCourse[end][:behavior] = BS[:type] push!(BS[:dataPoints], drivingCourse[end][:i]) @@ -1304,7 +1122,7 @@ end #function addBrakingSection! # 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::Train, CSs::Vector{Dict}) if drivingCourse[end][:v] == 0.0 - BS = createBehaviorSection("standstill", drivingCourse[end][:s], drivingCourse[end][:v], drivingCourse[end][:i]) + BS = BehaviorSection("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) @@ -1357,3 +1175,157 @@ function recalculateLastBrakingPoint!(drivingCourse, s_target, v_target) currentPoint[:ΔE] = currentPoint[:ΔW] # energy consumption in this step (in Ws) currentPoint[:E] = previousPoint[:E] + currentPoint[:ΔE] # energy consumption (in Ws) end #function recalculateLastBrakingPoint + +## 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::Train) + # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards + CSs = movingSection[:characteristicSections] + + CSs[1][:v_entry] = 0.0 # the entry velocity of the first characteristic section is 0.0 m/s + startingPoint = DataPoint() + 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::Train) + # limit the exit velocity of the characteristic sections in case that the train cruises in every section at v_peak + CSs = movingSection[:characteristicSections] + + startingPoint = DataPoint() + 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/calc.jl b/src/calc.jl index e9ed5ef..45649ea 100644 --- a/src/calc.jl +++ b/src/calc.jl @@ -15,7 +15,7 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t 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=DataPoint() startingPoint[:i]=1 startingPoint[:s]=CSs[1][:s_entry] calculateForces!(startingPoint, CSs, 1, "default", train, settings.massModel) # traction effort and resisting forces (in N) @@ -123,3 +123,196 @@ function calculateMinimumRunningTime!(movingSection::Dict, settings::Settings, t return (movingSection, drivingCourse) end #function calculateMinimumRunningTime + +""" + 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{}`: 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::Array{}) + 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::Train) + + if massModel == :mass_point + pathResistance = calcForceFromCoefficient(CSs[csId][:r_path], train.m_train_full) + elseif massModel == :homogeneous_strip + pathResistance = 0.0 + s_rear = s - train.length # position of the rear of the train + 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_full) + csId = csId-1 + if csId == 0 + # TODO: currently for values < movingSection[:s_entry] the values of movingSection[:s_entry] will be used + return pathResistance + (CSs[1][:s_entry] - s_rear) / train.length * calcForceFromCoefficient(CSs[1][:r_path], train.m_train_full) + 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::Train, 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.tractiveEffort)) + else # bsType == "accelerating" || bsType == "diminishing" || 'default' + dataPoint[:F_T] = calculateTractiveEffort(dataPoint[:v], train.tractiveEffort) + 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 = DataPoint() + 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{Tuple}, s::Real) + for s_POI in pointsOfInterest + if s_POI[1] > s + return s_POI + end + end + error("ERROR in getNextPointOfInterest: There is no POI higher than s=",s," m.") +end #function getNextPointOfInterest + + +## create a moving section and its containing characteristic sections with secured braking, accelerating and cruising behavior +function determineCharacteristics(path::Path, train::Train, settings::Settings) + movingSection = MovingSection(path, train.v_limit, train.length) + movingSection = secureBrakingBehavior!(movingSection, train.a_braking) + movingSection = secureAcceleratingBehavior!(movingSection, settings, train) + #movingSection = secureCruisingBehavior!(movingSection, settings, train) + + return movingSection +end #function determineCharacteristics diff --git a/src/characteristics.jl b/src/characteristics.jl deleted file mode 100644 index 7abcd10..0000000 --- a/src/characteristics.jl +++ /dev/null @@ -1,170 +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::Path, train::Train, settings::Settings) - movingSection = createMovingSection(path, train.v_limit, train.length) - movingSection = secureBrakingBehavior!(movingSection, train.a_braking) - movingSection = secureAcceleratingBehavior!(movingSection, settings, train) - #movingSection = secureCruisingBehavior!(movingSection, settings, train) - - return movingSection -end #function determineCharacteristics - -## 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::Train) - # this function limits the entry and exit velocity of the characteristic sections in case that the train accelerates in every section and cruises aterwards - CSs = movingSection[:characteristicSections] - - 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::Train) - # 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/constructors.jl b/src/constructors.jl index ffa27c1..a68f956 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -614,7 +614,7 @@ function Train(file, type = :YAML) end #function Train() # outer constructor ## create a moving section containing characteristic sections -function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) +function MovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real) # this function creates and returns a moving section dependent on the paths attributes s_entry = path.sections[1][:s_start] # first position (in m) @@ -631,12 +631,12 @@ function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real pathResistanceIsDifferent = previousSection[:f_Rp] != currentSection[:f_Rp] if speedLimitIsDifferent || pathResistanceIsDifferent # 03/09 old: if min(previousSection[:v_limit], v_trainLimit) != min(currentSection[:v_limit], v_trainLimit) || previousSection[:f_Rp] != currentSection[:f_Rp] - push!(CSs, createCharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path)) + push!(CSs, CharacteristicSection(csId, s_csStart, previousSection, min(previousSection[:v_limit], v_trainLimit), s_trainLength, path)) s_csStart = currentSection[:s_start] csId = csId+1 end #if end #for - push!(CSs, createCharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path)) + push!(CSs, CharacteristicSection(csId, s_csStart, path.sections[end], min(path.sections[end][:v_limit], v_trainLimit), s_trainLength, path)) movingSection= Dict(:id => 1, # identifier # if there is more than one moving section in a later version of this tool the id should not be constant anymore :length => pathLength, # total length (in m) @@ -647,10 +647,10 @@ function createMovingSection(path::Path, v_trainLimit::Real, s_trainLength::Real :characteristicSections => CSs) # list of containing characteristic sections return movingSection -end #function createMovingSection +end #function MovingSection ## create a characteristic section for a path section. A characteristic section is a part of the moving section. It contains behavior sections. -function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path) +function CharacteristicSection(id::Integer, s_entry::Real, section::Dict, v_limit::Real, s_trainLength::Real, path::Path) # Create and return a characteristic section dependent on the paths attributes characteristicSection= Dict(:id => id, # identifier :s_entry => s_entry, # first position (in m) @@ -690,15 +690,33 @@ function createCharacteristicSection(id::Integer, s_entry::Real, section::Dict, merge!(characteristicSection, Dict(:pointsOfInterest => pointsOfInterest)) return characteristicSection -end #function createCharacteristicSection +end #function CharacteristicSection + +""" +BehaviorSection() TODO! +""" +function BehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) + BS= Dict( + :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" + :length => 0.0, # total length (in m) + :s_entry => s_entry, # first position (in m) + :s_exit => 0.0, # last position (in m) + :t => 0.0, # total running time (in s) + :E => 0.0, # total energy consumption (in Ws) + :v_entry => v_entry, # entry speed (in m/s) + :v_exit => 0.0, # exit speed (in m/s) + :dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point + ) + return BS +end #function BehaviorSection """ a DataPoint is the smallest element of the driving course. One step of the step approach is between two data points """ -function createDataPoint() +function DataPoint() dataPoint = Dict( :i => 0, # identifier and counter variable of the driving course - :behavior => "", # type of behavior section the data point is part of - see createBehaviorSection() + :behavior => "", # type of behavior section the data point is part of - see BehaviorSection() # a data point which is the last point of one behavior section and the first point of the next behavior section will be attached to the latter :s => 0.0, # position (in m) :Δs => 0.0, # step size (in m) @@ -720,22 +738,4 @@ function createDataPoint() :label => "" # a label for importend points ) return dataPoint -end #function createDataPoint - -""" -BehaviorSection() TODO! -""" -function createBehaviorSection(type::String, s_entry::Real, v_entry::Real, startingPoint::Integer) - BS= Dict( - :type => type, # type of behavior section: "breakFree", "clearing", "accelerating", "cruising", "downhillBraking", "diminishing", "coasting", "braking" or "standstill" - :length => 0.0, # total length (in m) - :s_entry => s_entry, # first position (in m) - :s_exit => 0.0, # last position (in m) - :t => 0.0, # total running time (in s) - :E => 0.0, # total energy consumption (in Ws) - :v_entry => v_entry, # entry speed (in m/s) - :v_exit => 0.0, # exit speed (in m/s) - :dataPoints => [startingPoint] # list of identifiers of the containing data points starting with the initial point - ) - return BS -end #function createBehaviorSection +end #function DataPoint