Route Domain Model

Overview

Each international train has to be split into sections when the train crosses a border. The split locations are called handover points. At each handover the responsibility for the train planning changes from IM to IM and RU to RU. The planning process is coordinated by the RoutingInfo .lead_ru.

This UML class diagram 1 shows the main concepts involved:

class JourneyLocation <<TOM>> {
    id: LocationIdentifier
}
class Train <<TOM>> {
    +TrainID train_id()
}
class RoutingInfo {
    lead_ru: CompanyCode
}

class RouteSection {
    planning_im: CompanyCode
    applicant_ ru: CompanyCode
}
JourneyLocation <|-- Origin
JourneyLocation <|-- Handover
JourneyLocation <|-- Destination

RouteSection "*" -> "1" JourneyLocation : departure_station >
RouteSection "*" -> "1" JourneyLocation : arrival_station >
RoutingInfo *-- "1..*" RouteSection
RoutingInfo "0..1" -* Train : routing <

class PathRequest <<TOM>> {
    planning_im: CompanyCode
    applicant_ ru: CompanyCode
}
RouteSection o--  "0..*" PathRequest : planned for <

Overview Routing Model

An international Train 2 must at least have two sections. All RouteSections of a train are bundled in a RoutingInfo. The Lead RU is responsible for the correctness and communication of this information to all involved companies. The following constraint must hold for each RouteSection:

Rule SEC-JL Journey Locations

  • RouteSection.departure_station is a Origin or Handover

  • RouteSection.arrival_station is a Handover or Destination

For each RouteSection the planning_im and applicant_ru exchange PathRequest and PathDetails to plan the detailed route within a RouteSection. This process is described in detail in the Sector Handbook of the RU/IM Telematrics JSG. As long as borders of a section both in location and time are unchanged no other participant in the planning of the whole train has to be informed. See also Routing Planning Process.

The following paragraph describes the minimal information which all companies involved must know about the RoutingInfo. Changes to this information have to be propagated by the lead RU.

Train, RoutingInfo and RouteSection

The following diagramm shows the three main classes of our proposition for the Route Domain Model. It describes the minimum information needed for a valid route information of an international train:

left to right direction

class Train {
    core: CoreID
    timetable_year: Year
    +TrainID train_id()
}

class RoutingInfo {
    lead_ru: CompanyCode
    version: int
}

class RouteSection {
    applicant_ru: CompanyCode
    planning_im: CompanyCode
    ---
    departure_station: JourneyLocation
    arrival_station: JourneyLocation
    ---
    calendar: Set<Date>
    departure_time: TimeOfDay
    departure_stop_time: timedelta
    travel_time: timedelta
    ---
    version: int
    ---
    + String section_key()
    + String section_id()
}
note left of RouteSection
  The section_id() must be unique
  within the set of sections of a RoutingInfo
end note

Train *-- "0..1" RoutingInfo
RoutingInfo *-- "1..*" RouteSection

RoutingInfo and RouteSection

A train has zero or one RoutingInfo which bundles the information in RouteSections. The version is incremented each time one or more RouteSections have changed.

Timestamps

This model completely describes a planned set of train runs. Each train run is a sequence of section runs which have to connect properly in time at handover points. The timing attributes of a RouteSection serve this purpose:

  • calendar is a set of calendar days where the train starts at RouteSection.departure_station

  • departure_time is the day time the train starts each day in calendar

  • departure_stop_time planned stop time at a section handover station of departure

  • travel_time travel time from section station of departure to section station of arrival. This attribute is used to calculate the arrival time at station of arrival.

Only RouteSections have a calendar. This is the set of starting days at the departure_station of a section. All timestamps in Train, TrainRun and SectionRuns (see next chapter) are calculated from these timing attributes.

Rule SEC-CAL RouteSection Calendar must be subset of timetable year

The RouteSection.calendar must be a subset of the timetable year of its train.

Because a train can have several starting RouteSections (i.e. different departure stations and dates) it makes no sense to assign a calender to a train. A calender must always be relativ to a fixed start location. Example with three IMs explains such a situation with an overnight train in the starting sections.

Working with only timestamps also avoids the difficulties with overnight trains. No OTR (Offset To Reference) is needed!

Calendars

In our model these classes have a calendar:

  • A RouteSection defines the planned daily runs of a train

  • A PathRequest is planned for a RouteSection

  • A Path is created in response to a PathRequest of an RU to an IM

class RoutingInfo {
    lead_ru: CompanyCode
}

class RouteSection {
    calendar: Set<Date>
    + planning_im(): CompanyCode
    + applicant_ru(): CompanyCode
}

RoutingInfo *-- "1..*" RouteSection
RoutingInfo "0..1" -* Train : routing <

class PathRequest <<TOM>> {
    calendar: Set<Date>
    + planning_im(): CompanyCode
    + applicant_ru(): CompanyCode
}
RouteSection o-  "0..*" PathRequest : planned_for <
class Path <<TOM>> {
    calendar: Set<Date>
    + planning_im(): CompanyCode
    + applicant_ ru(): CompanyCode
}
PathRequest o-- "0..*" Path : response_to <

Calendars of RouteSection, PathRequest and Path

The following rules must hold for a consistent set of paths of a train:

Rule SEC-CAL Section-PathRequest-Path calendar relationships

  • The calendar of a PathRequest must be a subset of the calendar of the section it is planned for.

  • The calendar of a Path must be a subset of the calendar the PathRequest it is a response.

  • The calendars of two different PathRequests of a RouteSection must be pairwise disjoint.

  • The calendars of two different Paths of a PathRequest must be pairwise disjoint.

  • The calender of a RouteSection must be the union of the calendars of its PathRequests.

  • The calender of a PathRequest must be the union of the calendars of its Paths.

The last two rules must hold when the path allocation process is finished. The other must always hold. Objects that violate these conditions must not be communicated among partners. Such messages have to be answered with an ErrorMessage.

Identifiers

The method Train train_id() computes the unique trainID from core_id, lead_ru and timetable_year.

Rule SEC-UID Unique Section ID

RouteSection.section_id must be unique within the set of sections of a train.

The section_id is used to make the daily train IDs unique in case of overnight trains. Using the start date of a train results in duplicate train IDs. See example below.

Rule SEC-UFK Unique functional key of sections

A route section of a train is uniquely identified by this quadruple:

((section.departure_station, section.departure_time), (section.arrival_station, section.arrival_time))

Therefore, if on some day in the calendar of a section the train from section station of departure AC to section station of arrival EMM should arrive at at different time at EMM, the RU has to define a new RouteSection for this day. If EMM is a handover, the lead RU must ask the applicant RU of the following section to create fitting route section with section station of departure EMM.

Versioning

We suggest versioning of trains and sections to support the synchronisation of the domain model between the systems of the cooperating companies. If the lead RU changes a section its RouteSection.version is incremented. Same with the RoutingInfo.version. The receiver of a message containing the RoutingInfo 3 can use this information to identify the change and act accordingly.

TrainRun and SectionRun

In this section we describe, how the daily train (i.e. a TrainRun) and section runs are computed from the routing info of a Train described in the previous section.

  • A SectionRun is derived for each day in the calendar of its RouteSection

  • A TrainRun is computed from SectionRuns that fit together. Two sections s, t fit together if s.connects_to(t) == True (see connects_to()).

left to right direction

class Train <<TOM>> {
    +TrainID train_id()
}
class RoutingInfo {
    lead_ru: CompanyCode
}
class RouteSection {
    applicant_ru: CompanyCode
    planning_im: CompanyCode

    departure_time: TimeOfDay
    calendar: Set<Date>
}
Train *-- "0..1" RoutingInfo
RoutingInfo *-- "1..*" RouteSection

class SectionRun {
    departure_time: Timestamp
    + String section_id()
    + Timestamp arrival_time()
    + Timestamp arrival_at_departure_station()
    + JourneyLocation departure_station()
    + JourneyLocation arrival_station()
    + Boolean connects_to(other: SectionRun)
}
RouteSection *- "1..*" SectionRun
note bottom of SectionRun
    A section run happens on the calendar day
    of its departure_time timestamp.
end note

class TrainRun {
    + TrainID train_run_id()
    + Date start_date()
}
note bottom of TrainRun
    The start_date of a train run is the calendar day
    of the departure_time timestamp of
    its first section run.
end note

TrainRun o--> "1..* {ordered}" SectionRun
Train o- "1..365" TrainRun

SectionRun

For each day in day in RouteSection.calendar a section run is created. All SectionRuns of a section differ only in the value of departure_time(), which is the only information stored in a SectionRun. All other values shown can be computed from this timestamp and the attributes of the section the run belongs to.

TrainRun

Whereas the section runs can easily computed from information in its RouteSection (mainly calendar and departure time at section station of departure), the train runs must be computed from all possible SectionRun sequences that fit together.

We use a graph algorithm to compute all train runs. The graph used is best described by the examples shown below. The central methods are:

  • train_run_graph() which computes a directed Graph G = (SectionRun, E), where (s,t) in E <=> s.connects_to(t).

  • extended_train_run_graph() which adds to synthetic vertices to G, one at the beginning (START) connecting to departure stations of SectionRuns and one at the end (END) which connects to all stations of arrival of SectionRuns.

  • train_run_iterator() which computes all TrainRuns out of all path in G from START to END.

Footnotes

1

We use PlantUML as modeling tool. See explanations there.

2

Click on the link to see the python source code for the code element

3

Train and RouteSections could be transported in the TAF/TAP TrainInformation message structure.