Whether you would like to use OSRD, understand it or contribute, this is the right place!

This is the multi-page printable view of this section. Click here to print.

# Documentation

- 1: Tutorials
- 2: Explanations
- 2.1: Infrastructure data
- 2.2: Running time calculation
- 2.2.1: Physical modeling
- 2.2.2: Numerical integration
- 2.2.3: Envelopes system
- 2.2.4: Pipeline
- 2.2.5: Allowances
- 3: How-to Guides
- 3.1: Contribute to OSRD
- 3.1.1: Report issues
- 3.1.2: Contribute code
- 3.1.3: Style guide
- 3.1.4: Review code
- 4: Technical reference
- 4.1: Architecture
- 4.2: Design documents
- 4.2.1: Conflict detection
- 4.2.2: Search for last-minute train slots (STDCM)
- 4.2.2.1: Business context
- 4.2.2.2: Train slot search module
- 4.2.2.2.1: Input format
- 4.2.2.2.2: Encoding the solution space
- 4.2.2.2.3: Discontinuities and backtracking
- 4.2.2.2.4: Conflict avoidance
- 4.2.2.2.5: Standard allowance
- 4.2.2.2.6: Implementation details
- 4.2.2.3: Signaling interface
- 4.3: APIs
- 4.3.1: JSON Schemas
- 5: Railway Wiki
- 5.1: Glossary

# 1 - Tutorials

Tutorials take you by the hand through a series of steps to complete small projects. Start here if you’re new to OSRD. Also look at the “First steps”.

# 2 - Explanations

Explanations discuss key topics and concepts at a fairly high level and provide useful background information and explanation.

# 2.1 - Infrastructure data

### Introduction

This page gives an example of how the data formats are used to describe an infrastructure in **OSRD**.

For this purpose, let’s take as an example the following toy infrastructure:

#### Tip

To zoom in on diagrams, click on the edit button that appears when hovering over it.This diagram is an overview of the infrastructure with lines and stations only.

This infrastructure is not meant to be realistic, but rather meant to help illustrate OSRD’s data model. This example will be created step by step and explained along the way.

#### The infrastructure generator

In the *OSRD* repository is a python library designed to help generate infrastructures in a format understood by *OSRD*.

The infrastructure discussed in this section can be generated thanks to small_infra.py file. To learn more about the generation scripts, you can check out the related README.

### Tracks

#### Track sections

The first objects we need to define are `TrackSections`

. Most other objects are positioned relative to track sections.

A track section is a section of rail (switches not included). One can chose to divide the tracks of their infrastructure in as many track sections as they like. Here we chose to use the longest track sections possible, which means that between two switches there is always a single track section.

Track sections is what simulated trains roll onto. They are the abstract equivalent to physical rail sections. Track sections are bidirectional.

In this example, we define two tracks for the line between the West and North-East stations. We also have overpassing tracks at the North and Mid-West stations for added realism. Finally, we have three separate tracks in the West station, since it’s a major hub in our imaginary infrastructure.

#### Important

`TrackSections`

are represented as arrows in this diagram to stress the fact that they have a **start** and an **end**. It matters as objects positioned on track sections are located using their distance from the **start** of their track section.

Therefore, to locate an object at the beginning of its track section, set its offset to 0. To move it to the end of its track section, set its offset to the `length`

of the track section.

These attributes are required for the track section to be complete:

`length`

: the length of the track section in meters.`geo`

: the coordinates in real life (geo is for geographic), in the GeoJSON format.`sch`

: the coordinates in the schematic view (sch for schematic), also in GeoJSON format.- cosmetic attributes:
`line_name`

,`track_name`

,`track_number`

which are used to indicate the name and labels that were given to the tracks / lines in real life.

For all track sections in our infrastructure, the `geo`

and `sch`

attributes are identical, and very much resemble the given diagram.

For most track sections, their `length`

is proportional to what can be seen in the diagram. To preserve readability, exceptions were made for *TA6*, *TA7*, *TD0* and *TD1* (which are 10km and 25km).

#### Track Section Links

At the moment we only created track sections, which are not connected to each other (geospacial data is not used to deduce which tracks connect).

`TrackSectionLinks`

are used to connect two track sections together, just like a weld joint would in real life. In an OSRD simulation, a train can go from one track section to another only if they are connected by a `TrackSectionLink`

(or by a `Switch`

).

To connect more than two `TrackSections`

together, use the `Switches`

.

In our infrastructure, since we chose to have our track sections as long as possible, we do not actually need to use `TrackSectionLinks`

.
`TrackSectionLinks`

are always optional: two track sections connected by a link behave just like a single track section.

#### Switches

A `Switch`

represents just what you would expect: railway switches.

Switches can be thought of as a collections of track section links, partitioned into groups. Each group represents for a switch state. Switching group can take time, and at most one link can be ready to use at a time.

In the real world, switches are not unique, but rather instances of existing models.
Thus, links and groups are not part of the switch itself, but in a `SwitchType`

object, which is shared by switches of the same model.

##### Switch Types

`SwitchTypes`

have two mandatory attributes:

`ports`

: A list of port names. A port is an endpoint which can be connected to a track section.`groups`

: A mapping from group names to lists of links between two ports.

At any time, all switches have an active group, and may have an active link, which always belongs to the active group. If there is an active link, it is active in a given direction. During a simulation, changing active link inside a group is instantaneous, but changing active link across groups takes configurable time.
This is because a switch is a physical object, and changing active link can involve moving parts of it. `Groups`

are designed to represent the different positions that a switch can have. Each `group`

contains the links that can be used in the associated switch position.

The duration needed to change group is stored inside the `Switch`

, since it can vary depending on the physical implementation of the switch.

Our examples currently use three switch types. Switch types are just like other objects, and can easily be added as needed.

**1) The Point Switch**

The ubiquitous Y switch, which can be thought of as either two tracks merging, or one track splitting.

This switch type has three ports: *base*, *left* and *right*.

There are two groups, each with one connection in their list: `LEFT`

, which connects *base* to *left*, and `RIGHT`

which connects *base* to *right*.

Thus, at any given moment, a train can go from *base* to *left* or from *base* to *right* but never to both at the same time. A train cannot go from *left* to *right*.

A Point Switch only has two positions:

**2) The Cross Switch**

This is simply two tracks crossing each other.

This type has four ports: *north*, *south*, *east* and *west*.

It has only one group containing two connections: *north* to *south* and *west* to *east*. Indeed this kind of switch is *passive*: it has no moving parts. Despite having a single group, it is still used by the simulation to enforce route reservations.

Here are the two different connections that this switch type has:

**3) The Double cross switch**

This one is more like two point switches back to back. It has four ports: *south1*, *south2*, *north1* and *north2*.

However, it has four groups, each with one connection. The four groups are represented in the following diagram:

##### Back to switches

A `Switch`

has three attributes:

`switch_type`

: the`SwitchType`

identifier for this switch.`ports`

: a mapping from port names to track sections extremities.`group_change_delay`

: the time it takes to change which group of the switch is activated.

The port names must match the ports of the switch type chosen. The track section endpoints can be start or end, be careful to chose the appropriate ones.

Most of our example’s switches are regular point switches. The path from North station to South station has two cross switches, and there is a double cross switch right before the main line splits into the North-East and South-East lines.

#### Curves and slopes

`Curves`

and `Slopes`

are instrumental to realistic simulations. These objects are defined as a range between a `begin`

and `end`

offsets of one track section. If a curve / slope spans more than one track section, it has to be added to all of them.

The slope / curve values are constant on their entire range. For varying curves / slopes, one needs to create several objects.

Slope values are measured in *meters per kilometers*, and the curve values are measured in *meters* (the radius of the curve).

`begin`

value should always be smaller than the `end`

value. That is why the curve / slope values can be negative: an uphill slope of 1 going from offset 10 to 0 is the same as a downhill slope of -1 going from offsets 0 to 10.In the small_infra.py file, we have slopes on the track sections *TA6*, *TA7*, *TD0* and *TD1*.

There are curves as well, on the track sections *TE0*, *TE1*, *TE3* and *TF1*.

### Interlocking

All objects so far contributed to track topology (shape). Topology would be enough for trains to navigate the network, but not enough to do so safely. to ensure safety, two systems collaborate:

- Interlocking ensures trains are allowed to move forward
- Signaling is the mean by which interlocking communicates with the train

#### Detectors

These objects are used to create TVD sections (Track Vacancy Detection section): the track area in between detectors is a TVD section. When a train runs into a detector, the section it is entering becomes occupied. The only function of TVD sections is to locate trains.

In real life, detectors can be axle counters or track circuits for example.

For this mean of location to be efficient, detectors need to be placed regularly along your tracks, not too many because of cost, but not too few, because then TVD sections would be very large and trains would need to be very far apart to be told apart, which reduces capacity.

There often are detectors close to all sides of switches. This way, interlocking is made aware pretty much immediately when a switch is cleared, which is then free to be used again.

*north*to

*south*and train B is coming to cross

*west*to

*east*, then as soon as train A’s last car has passed the crossing, B should be able to go, since A is now on a completely unrelated track section.

In *OSRD*, detectors are point objects, so all the attributes it needs are its `id`

, and track location (`track`

and `offset`

).

Some notes:

- Between some points, we added only one detector (and not two), because they were really close together, and it would have made no sense to create a tiny TVDS between the two. This situation happened on track sections (
*TA3*,*TA4*,*TA5*,*TF0*and*TG3*). - In our infrastructure, there is relatively few track sections which are long enough to require more detectors than just those related to switches. Namely,
*TA6*,*TA7*,*TDO*,*TD1*,*TF1*,*TG1*and*TH1*. For example*TD0*, which measures 25km, has in fact 17 detectors in total.

#### Buffer stops

`BufferStops`

are obstacles designed to prevent trains from sliding off dead ends.

In our infrastructure, there is a buffer stop on each track section which has a loose end. There are therefore 8 buffer stops in total.

Together with detectors, they set the boundaries of TVD sections (see Detectors)

#### Routes

A `Route`

is an itinerary in the infrastructure. A train path is a sequence of routes. Routes are used to reserve section of path with the interlocking. See the dedicated documentation.

It is represented with the following attributes:

`entry_point`

and`exit_point`

: references detectors or buffer stops which mark the beginning and the end of the Route.`entry_point_direction`

: Direction on a track section to start the route from the`entry_point`

.`switches_direction`

: A set of directions to follow when we encounter a switch on our Route, to build this Route from`entry_point`

to`exit_point`

.`release_detectors`

: When a train clears a release detector, resources reserved from the beginning of the route until this detector are released.

### Signaling

Thanks to interlocking, trains are located and allowed to move. It’s a good start, but meaningless until trains are made aware of it. This is where `Signal`

s come into play: signals react to interlocking, and can be seen by trains.

How trains react to signals depends on the aspect, kind of signal, and signaling system.

Here are the most important attributes for signals:

`linked_detector`

: The linked detector.`type_code`

: The type of signal.`direction`

: The direction it protects, which can simply be interpreted as the way in which it can be seen by an incoming train (since there are lights only on one side…). Direction is relative to track section orientation.- Cosmetic attributes like
`angle_geo`

or`side`

which control the way in which the signals are displayed in the front-end.

Here is a visualization of how one can represent a signal, and which direction it protects.

The way the signals are arranged is highly dependent on both signaling system and infrastructure manager.

Here are the basic rules used for this example infrastructure:

- We add two spacing signals (one per direction) for each detector that is cutting a long TVD section into smaller ones.
- Switch entries where a train might have to stop are protected by a signal (which is located outside of the switch TVD section). It must be visible from the direction used to approach the switch. When there are multiple switches in a row, only the first one usually needs protection, as interlocking is usually designed as not to encourage trains stopping in the middle of intersections.

Note that detectors linked to at least one signal are not represented, as there are not signals without associated detectors in this example.
To get the `id`

of a detector linked to a signal, take the signal’s `id`

and replace *S* by *D* (e.g. SA0 -> DA0).

*TA6*,

*TA7*,

*TD0*and

*TD1*we could not represent all signals because these track sections are very long and have many detectors, hence many signals.

## Miscellaneous

#### Operational points

`OperationalPoints`

are collections of points (`OperationalPointParts`

) of interest.

For example, it may be convenient to store the location of platforms as parts and group them by station in operational points.

In the example infrastructure, we only used operational points to represent stations. Operational point parts are displayed as purple diamonds. Keep in mind a single operational point may contain multiple parts.

#### Loading Gauge Limits

These objects are akin to `Slopes`

and `Curves`

: it covers a range of track section, with a `begin`

and an `end`

offset. It represents a restriction on the trains that can travel on the given range, by weight or by train type (freight or passenger).

We did not put any in our examples.

#### Speed Sections

The `SpeedSections`

represent speed limits (in meters per second) that are applied on some parts of the tracks. One `SpeedSection`

can span on several track sections, and do not necessarily cover the whole track sections. Speed sections can overlap.

In our example infrastructure, we have a speed section covering the whole infrastructure, limiting the speed to 300 km/h. On a smaller part of the infrastructure, we applied more restrictive speed sections.

# 2.2 - Running time calculation

OSRD can be used to perform two types of calculations:

**Standalone train simulation:**calculation of the travel time of a train on a given route without interaction between the train and the signalling system.**Simulation:**“dynamic” calculation of several trains interacting with each other via the signalling system.

#### 1 - The input data

A running time calculation is based on 5 inputs:

**Infrastructure:**Line and track topology, position of stations and passenger buildings, position and type of points, signals, maximum line speeds, corrected line profile (gradients, ramps and curves).

The blue histogram is a representation of the gradients in [‰] per position in [m]. The gradients are positive for ramps and negative for slopes.

The orange line represents the cumulative profile, i.e. the relative altitude to the starting point.

The blue line is a representation of turns in terms of radii of curves in [m].

**The rolling stock:**The characteristics of which needed to perform the simulation are shown below.

The orange curve, called the effort-speed curve, represents the maximum motor effort as a function of the speed of travel.

The length, mass, and maximum speed of the train are shown at the bottom of the box.

**The departure time**is then used to calculate the times of passage at the various points of interest (including stations).**Allowances:**Time added to the train’s journey to relax its running (see page on allowances).

**The time step**for the calculation of numerical integration.

#### 2 - The results

The results of a running time calculation can be represented in different forms:

**The space/time graph (GET):**represents the path of trains in space and time, in the form of generally diagonal lines whose slope is the speed. Stops are shown as horizontal plates.

Example of a GET with several trains spaced about 30 minutes apart.

The

xaxis is the time of the train, theyaxis is the position of the train in [m].The blue line represents the most tense running calculation for the train, the green line represents a relaxed, so-called “economic” running calculation.

The solid rectangles surrounding the paths represent the portions of the track successively reserved for the train to pass (called blocks).

**The space/speed graph (SSG):**represents the journey of a single train, this time in terms of speed. Stops are therefore shown as a drop in the curve to zero, followed by a re-acceleration.

The

xaxis is the train position in [m], theyaxis is the train speed in [km/h].The purple line represents the maximum permitted speed.

The blue line represents the speed in the case of the most stretched running calculation.

The green line represents the speed in the case of the “economic” travel calculation.

**The timetable for the passage of the train at the various points of interest**.

# 2.2.1 - Physical modeling

Physical modelling plays an important role in the OSRD core calculation. It allows us to simulate train traffic, and it must be as realistic as possible train traffic, and it must be as realistic as possible.

### Force review

To calculate the displacement of the train over time, we must first calculate its speed at each instant. A simple way to obtain this speed is to calculate the acceleration. Thanks to the fundamental principle of dynamics, the acceleration of the train at each instant is directly dependent on the different forces applied to it: $$ \sum \vec{F}=m\vec{a} $$

**Traction**: The value of the traction force \(F_{mot}\) depends on several factors:- the rolling stock
- the speed of the train, \(v^{\prime}x\) according to the effort-speed curve below:

$$ {\vec{F_{mot}}(v_{x^{\prime}}, x^{\prime})=F_{mot}(v_{x^{\prime}}, x^{\prime})\vec{e_x^{\prime}}} $$

The

**x**axis represents the speed of the train in [km/h], the**y**axis the value of the traction force in [kN].- the action of the driver, who accelerates more or less strongly depending on where he is on his journey

**Braking**: The value of the braking force \(F_{brk}\) also depends on the rolling stock and the driver’s action but has a constant value for a given rolling stock. In the current state of modelling, braking is either zero or at its maximum value.

$$ \vec{F_{brk}}(x^{\prime})=-F_{brk}(x^{\prime}){\vec{e_{x^{\prime}}}} $$

A second approach to modelling braking is the so-called hourly approach, as it is used for hourly production at SNCF. In this case, the deceleration is fixed and the braking no longer depends on the different forces applied to the train. Typical deceleration values range from 0.4 to 0.7m/s².

**Forward resistance**: To model the forward resistance of the train, the Davis formula is used, which takes into account all the friction and aerodynamic resistance of the air. The value of the drag depends on the speed \(v^{\prime}_x\). The coefficients \(A\), \(B\), et \(C\) depend on the rolling stock.

$$ {\vec{R}(v_{x^{\prime}})}=-(A+Bv_{x^{\prime}}+{Cv_{x^{\prime}}}^2){\vec{e_{x^{\prime}}}} $$

**Weight (slopes + turns)**: The weight force given by the product between the mass \(m\) of the train and the gravitational constant \(g\) is projected on the axes \(\vec{e_x}^{\prime}\) and \(\vec{e_y}^{\prime}\).For projection, we use the angle \(i(x^{\prime})\), which is calculated from the slope angle \(s(x^{\prime})\) corrected by a factor that takes into account the effect of the turning radius \(r(x^{\prime})\).

$$ \vec{P(x^{\prime})}=-mg\vec{e_y}(x^{\prime})= -mg\Big[sin\big(i(x^{\prime})\big){\vec{e_{x^{\prime}}}(x^{\prime})}+cos\big(i(x^{\prime})\big){\vec{e_{{\prime}}}(x^{\prime})}\Big] $$

$$ i(x^{\prime})= s(x^{\prime})+\frac{800m}{r(x^{\prime})} $$

**Ground Reaction**: The ground reaction force simply compensates for the vertical component of the weight, but has no impact on the dynamics of the train as it has no component along the axis \({\vec{e_x}^{\prime}}\).

$$ \vec{R_{gnd}}=R_{gnd}{\vec{e_{y^{\prime}}}} $$

### Forces balance

The equation of the fundamental principle of dynamics projected onto the axis \({\vec{e_x}^{\prime}}\) (in the train frame of reference) gives the following scalar equation:

$$ a_{x^{\prime}}(t) = \frac{1}{m}\Big [F_{mot}(v_{x^{\prime}}, x^{\prime})-F_{brk}(x^{\prime})-(A+Bv_{x^{\prime}}+{Cv_{x^{\prime}}}^2)-mgsin(i(x^{\prime}))\Big] $$

This is then simplified by considering that despite the gradient the train moves on a plane and by amalgamating \(\vec{e_x}\) and \(\vec{e_x}^{\prime}\). The gradient still has an impact on the force balance, but it is assumed that the train is only moving horizontally, which gives the following simplified equation:

$$ a_{x}(t) = \frac{1}{m}\Big[F_{mot}(v_{x}, x)-F_{brk}(x)-(A+Bv_{x}+{Cv_{x}}^2)-mgsin(i(x))\Big] $$

### Resolution

The driving force and the braking force depend on the driver’s action (he decides to accelerate or brake more or less strongly depending on the situation). This dependence is reflected in the dependence of these two forces on the position of the train. The weight component is also dependent on the position of the train, as it comes directly from the slopes and bends below the train.

In addition, the driving force depends on the speed of the train (according to the speed effort curve) as does the resistance to forward motion. resistance.

These different dependencies make it impossible to solve this equation analytically, and the acceleration of the train at each moment must be calculated by numerical integration.

# 2.2.2 - Numerical integration

### Introduction

Since physical modelling has shown that the acceleration of the train is influenced by various factors that vary along the route (gradient, curvature, engine traction force, etc.), the calculation must be carried out using a numerical integration method. The path is then separated into sufficiently short steps to consider all these factors as constant, which allows this time to use the equation of motion to calculate the displacement and speed of the train.

Euler’s method of numerical integration is the simplest way of doing this, but it has a number of drawbacks. This article explains the Euler method, why it is not suitable for OSRD purposes and which integration method should be used instead.

### Euler’s method

The Euler method applied to the integration of the equation of motion of a train is:

$$v(t+dt) = a(v(t), x(t))dt + v(t)$$

$$x(t+dt) = \frac{1}{2}a(v(t), x(t))dt^2 + v(t)dt + x(t)$$

**Advantages of Euler’s method**

The advantages of the Euler method are that it is very simple to implement and has a rather fast calculation for a given time step, compared to other numerical integration methods (see appendix)

**Disadvantages of the Euler’s method**

The Euler integration method presents a number of problems for OSRD:

- It is relatively imprecise, and therefore requires a small time step, which generates a lot of data.
- With time integration, only the conditions at the starting point of the integration step (gradient, infrastructure parameters, etc.) are known, as one cannot predict precisely where it will end.
- We cannot anticipate future changes in the directive: the train only reacts by comparing its current state with its set point at the same time. To illustrate, it is as if the driver is unable to see ahead, whereas in reality he anticipates according to the signals, slopes and bends he sees ahead.

### Runge-Kutta’s 4 method

The Runge-Kutta 4 method applied to the integration of the equation of motion of a train is:

$$v(t+dt) = v(t) + \frac{1}{6}(k_1 + 2k_2 + 2k_3 + k_4)dt$$

With:

$$k_1 = a(v(t), x(t))$$

$$k_2 = a\Big(v(t+k_1\frac{dt}{2}), x(t) + v(t)\frac{dt}{2} + k_1\frac{dt^2}{8}\Big)$$

$$k_3 = a\Big(v(t+k_2\frac{dt}{2}), x(t) + v(t)\frac{dt}{2} + k_2\frac{dt^2}{8}\Big)$$

$$k_4 = a\Big(v(t+k_3dt), x(t) + v(t)dt + k_3\frac{dt^2}{2}\Big)$$

**Advantages of Runge Kutta’s 4 method**

Runge Kutta’s method of integration 4 addresses the various problems raised by Euler’s method:

- It allows the anticipation of directive changes within a calculation step, thus representing more accurately the reality of driving a train.
- It is more accurate for the same calculation time (see appendix), allowing for larger integration steps and therefore fewer data points.

**Disavantages of Runge Kutta’s 4 method**

The only notable drawback of the Runge Kutta 4 method encountered so far is its difficulty of implementation.

### The choice of integration method for OSRD

**Study of accuracy and speed of calculation**

Different integration methods could have replaced the basic Euler integration in the OSRD algorithm. In order to decide which method would be most suitable, a study of the accuracy and computational speed of different methods was carried out. This study compared the following methods:

- Euler
- Euler-Cauchy
- Runge-Kutta 4
- Adams 2
- Adams 3

All explanations of these methods can be found (in french) in this document, and the python code used for the simulation is here.

The simulation calculates the position and speed of a high-speed train accelerating on a flat straight line.

**Equivalent time step simulations**

A reference curve was simulated using the Euler method with a time step of 0.1s, then the same path was simulated using the other methods with a time step of 1s. It is then possible to simply compare each curve to the reference curve, by calculating the absolute value of the difference at each calculated point. The resulting absolute error of the train’s position over its distance travelled is as follows:

It is immediately apparent that the Euler method is less accurate than the other four by about an order of magnitude. Each curve has a peak where the accuracy is extremely high (extremely low error), which is explained by the fact that all curves start slightly above the reference curve, cross it at one point and end slightly below it, or *vice versa*.

As accuracy is not the only important indicator, the calculation time of each method was measured. This is what we get for the same input parameters:

Integration method | Calculation time (s) |
---|---|

Euler | 1.86 |

Euler-Cauchy | 3.80 |

Runge-Kutta 4 | 7.01 |

Adams 2 | 3.43 |

Adams 3 | 5.27 |

Thus, Euler-Cauchy and Adams 2 are about twice as slow as Euler, Adams 3 is about three times as slow, and RK4 is about four times as slow. These results have been verified on much longer simulations, and the different ratios are maintained.

**Simulation with equivalent calculation time**

As the computation times of all methods depend linearly on the time step, it is relatively simple to compare the accuracy for approximately the same computation time. Multiplying the time step of Euler-Cauchy and Adams 2 by 2, the time step of Adams 3 by 3, and the time step of RK4 by 4, here are the resulting absolute error curves:

And here are the calculation times:

Integration method | Calculation time (s) |
---|---|

Euler | 1.75 |

Euler-Cauchy | 2.10 |

Runge-Kutta 4 | 1.95 |

Adams 2 | 1.91 |

Adams 3 | 1.99 |

After some time, RK4 tends to be the most accurate method, slightly more accurate than Euler-Cauchy, and still much more accurate than the Euler method.

### Conclusions of the study

The study of accuracy and computational speed presented above shows that RK4 and Euler-Cauchy would be good candidates to replace the Euler algorithm in OSRD: both are fast, accurate, and could replace the Euler method without requiring large implementation changes because they only compute within the current time step.
**It was decided that OSRD would use the Runge-Kutta 4 method because it is slightly more accurate than Euler-Cauchy and it is a well-known method for this type of calculation, so it is very suitable for an open-source simulator.**

# 2.2.3 - Envelopes system

The envelope system is an interface created specifically for the OSRD gait calculation. It allows you to manipulate different space/velocity curves, to slice them, to end them, to interpolate specific points, and to address many other needs necessary for the gait calculation.

### A specific interface in the OSRD Core service

The envelope system is part of the core service of OSRD (see software architecture).

Its main components are :

**1 - EnvelopePart:** space/speed curve, defined as a sequence of points and having metadata indicating for example if it is an acceleration curve, a braking curve, a speed hold curve, etc.

**2 - Envelope:** a list of end-to-end EnvelopeParts on which it is possible to perform certain operations:

- check for continuity in space (mandatory) and speed (optional)
- look for the minimum and/or maximum speed of the envelope
- cut a part of the envelope between two points in space
- perform a velocity interpolation at a certain position
- calculate the elapsed time between two positions in the envelope

**3 - Overlays :** system for adding more constrained (i.e. lower speed) EnvelopeParts to an existing envelope.

### Given envelopes vs. calculated envelopes

During the simulation, the train is supposed to follow certain speed instructions. These are modelled in OSRD by envelopes in the form of space/speed curves. Two types can be distinguished:

- Envelopes from
**infrastructure and rolling stock data**, such as maximum line speed and maximum train speed. Being input data for our calculation, they do not correspond to curves with a physical meaning, as they are not derived from the results of a real integration of the physical equations of motion. - The envelopes result from
**real integration**of the physical equations of motion. They correspond to a curve that is physically tenable by the train and also contain time information.

A simple example to illustrate this difference: if we simulate a TER journey on a mountain line, one of the input data will be a maximum speed envelope of 160km/h, corresponding to the maximum speed of our TER. However, this envelope does not correspond to a physical reality, as it is possible that on certain sections the gradient is too steep for the train to be able to maintain this maximum speed of 160km/h. The calculated envelope will therefore show in this example a speed drop in the steepest areas, where the envelope given was perfectly flat.

### Simulation of several trains

In the case of the simulation of many trains, the signalling system must ensure **safety**. The effect of signalling on the running calculation of a train is reproduced by superimposing dynamic envelopes on the static envelope. A new dynamic envelope is introduced for example when a signal closes. The train follows the static economic envelope superimposed on the dynamic envelopes, if any. In this simulation mode, a time check is performed against a theoretical time from the time information of the static economic envelope. If the train is late with respect to the scheduled time, it stops following the economic envelope and tries to go faster. Its space/speed curve will therefore be limited by the maximum effort envelope.

# 2.2.4 - Pipeline

The walk calculation in OSRD is a 4-step process, each using the envelopes system:

- Construction of the most restrictive speed profile
- Addition of the different braking curves
- Adding the different acceleration curves and checking the constant speed curves
- Application of allowance(s)

### Calculation of the Most Restricted Speed Profile (MRSP)

A first envelope is calculated at the beginning of the simulation by grouping all static velocity limits:

- maximum line speed
- maximum speed of rolling stock
- temporary speed limits (e.g. in case of works on a line)
- speed limits by train category
- speed limits according to train load
- speed limits corresponding to signposts

The length of the train is also taken into account to ensure that the train does not accelerate until its tail leaves the slowest speed zone. An offset is then applied to the red dashed curve. The resulting envelope (black curve) is called the **Most Restricted Speed Profile (MRSP)**. It is on this envelope that the following steps will be calculated.

The red dotted line represents the maximum permitted speed depending on the position. The black line represents the MRSP where the train length has been taken into account.

It should be noted that the different envelopeParts composing the MRSP are input data, so they do not correspond to curves with a physical reality.

### Calculation of the Max Speed Profile

Starting from the MRSP, all braking curves are calculated using the overlay system (see here for more details on overlays), i.e. by creating envelopeParts which will be more restrictive than the MRSP. The resulting curve is called **Max Speed Profile**. This is the maximum speed envelope of the train, taking into account its braking capabilities.

Since braking curves have an imposed end point and the braking equation has no analytical solution, it is impossible to predict their starting point. The braking curves are therefore calculated backwards from their target point, i.e. the point in space where a certain speed limit is imposed (finite target speed) or the stopping point (zero target speed).

For historical reasons in hourly production, braking curves are calculated at SNCF with a fixed deceleration, the so-called hourly deceleration (typically ~0.5m/s²) without taking into account the other forces. This method has therefore also been implemented in OSRD, allowing the calculation of braking in two different ways: with this hourly rate or with a braking force that is simply added to the other forces.

### Calculation of the Max Effort Profile

For each point corresponding to an increase in speed in the MRSP or at the end of a stop braking curve, an acceleration curve is calculated. The acceleration curves are calculated taking into account all active forces (traction force, driving resistance, weight) and therefore have a physical meaning.

For envelopeParts whose physical meaning has not yet been verified (which at this stage are the constant speed running phases, always coming from the MRSP), a new integration of the equations of motion is performed. This last calculation is necessary to take into account possible speed stalls in case the train is physically unable to hold its speed, typically in the presence of steep ramps (see this example).

The envelope that results from the addition of the acceleration curves and the verification of the speed plates is called the **Max Effort Profile**.

At this stage, the resulting envelope is continuous and has a physical meaning from start to finish. The train accelerates to the maximum, runs as fast as possible according to the different speed limits and driving capabilities, and brakes to the maximum. The resulting travel calculation is called the **basic running time**. It corresponds to the fastest possible route for the given rolling stock on the given route.

### Application of allowance(s)

After the calculation of the basic run (corresponding to the Max Effort Profile in OSRD), it is possible to apply allowances. Allowances are additions of extra time to the train’s journey. They are used to allow the train to catch up if necessary or for other operational purposes (more details on allowances here).

A new **Allowances** envelope is therefore calculated using overlays to distribute the allowance requested by the user over the maximum effort envelope calculated previously.

In the OSRD running calculation it is possible to distribute the allowances in a linear way, by lowering all speeds by a certain factor, or in an economic way, i.e. by minimising the energy consumption during the train run.

# 2.2.5 - Allowances

### The purpose of allowances

As explained in the calcul du Max Effort Profile, the **basic running time** represents the most stretched run normally achievable, i.e. the fastest possible run of the given equipment on the given route. The train accelerates to the maximum, travels as fast as possible according to the different speed limits and driving capabilities, and brakes to the maximum.

This basic run has a major disadvantage: if a train leaves 10 minutes late, it will arrive at best 10 minutes late, because by definition it is impossible for it to run faster than the basic run. Therefore, trains are scheduled with one or more allowances added. The allowances are a relaxation of the train’s route, an addition of time to the scheduled timetable, which inevitably results in a lowering of running speeds.

A train running in basic gear is unable to catch up!

### Allowances types

There are two types of allowances:

**The regularity allowance**: this is the additional time added to the basic running time to take account of the inaccuracy of speed measurement, to compensate for the consequences of external incidents that disrupt the theoretical run of trains, and to maintain the regularity of the traffic. The regularity allowance applies to the whole route, although its value may change at certain intervals.**The construction allowance**: this is the time added/removed on a specific interval, in addition to the regularity allowance, but this time for operational reasons (dodging another train, clearing a track more quickly, etc.)

A basic running time with an added allowance of regularity gives what is known as a **standard walk**.

### Allowance distribution

Since the addition of allowance results in lower speeds along the route, there are a number of possible routes. Indeed, there are an infinite number of solutions that result in the same journey time.

As a simple example, in order to reduce the running time of a train by 10% of its journey time, it is possible to extend any stop by the time equivalent to this 10%, just as it is possible to run at 1/1.1 = 90.9% of the train’s capacity over the entire route, or to run slower, but only at high speeds…

There are currently two algorithms for margin distribution in OSRD: linear and economic.

### Linear distribution

Linear allowance distribution is simply lowering the speeds by the same factor over the area where the user applies the allowance. Here is an example of its application:

The advantage of this distribution is that the allowance is spread evenly over the entire journey. A train that is late on 30% of its journey will have 70% of its allowance for the remaining 70% of its journey.

### Economic distribution

The economic distribution of the allowance, presented in detail in this document (**MARECO** is an algorithm designed by the SNCF research department), consists of distributing the allowance in the most energy-efficient way possible. It is based on two principles:

- a maximum speed, avoiding the most energy-intensive speeds
- run-on zones, located before braking and steep gradients, where the train runs with the engine off thanks to its inertia, allowing it to consume no energy during this period

An example of economic walking. Above, the gradients/ramps encountered by the train. The areas of travel on the track are shown in blue.

# 3 - How-to Guides

How-to guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are more advanced than tutorials and assume some knowledge of how OSRD works.

# 3.1 - Contribute to OSRD

First off, thanks for taking the time to contribute!

The following chapters are a set of guidelines for contributing to OSRD^{1}. If you have already contributed to open source projects before, you probably won’t be surprised.
If you have not, it will probably help a lot!

### Communicate

Chatting with other contributors is a great way to speed things up:

- Join our instant messaging room on IRC at libera.chat#osrd
**Create an issue**to discuss your contribution project

### Inquire

Just like with any project, changes rely on past work. Before making changes, it is best to learn about what’s already there:

- read technical documentation
- read the existing source code related to your project
- chat with developers who last worked on areas you are interested in

These guidelines are mostly not strict rules, it’s probably fine to do things slightly differently. ↩︎

# 3.1.1 - Report issues

**Please report anything you deem significant!**

Our bug tracking platform is github, so you have to register to report bugs.

Follow this link and pick whatever template fits the best.

### Bugs

- Bug must have a correct description and the bug’s issue template must be filled carefully.
- Bug must be tagged with (
*for team members*):`kind:bug`

- one or several
`area:<affected_area>`

if possible, if the affected area is not known leave it blank it will be added later by another team member. - one
`severity:<bug_severity>`

if possible, if severity is not known leave it blank it will be added later by another team member.`severity:minor`

: User can still use the feature.`severity:major`

: User sometimes can’t use the feature.`severity:critical`

: User can’t use the feature.

- OSRD team members can change issues’ tags (severity, area, kind, …). You may leave a comment to explain changes.
- If you are working on a bug or plan to work on a bug, assign yourself to the bug.
- PRs solving bugs should add a regression tests to ensure that bug will not be back in the future.

# 3.1.2 - Contribute code

## Set things up

### Getting the source code

- Install
`git`

.^{1} - Open a terminal
^{2}in the folder where the source code of OSRD will be located - Run
`git clone git@github.com:DGEXSolutions/osrd`

### Launch the application with docker-compose

For a long time, making changes to a component of a multi-service application involved compiling, configuring and running all services manually.

Nowadays, it can be done using `docker compose`

. You can even start only a subset of the services.

- Install
`docker`

and`docker compose`

.^{1} - Run
`docker compose up --build`

## Share your changes

The source code of OSRD is available under the LGPLv3 license. By contributing to the codebase, you consent to the distribution of your changes under the project’s license.

LGPLv3 forbids modifying source code without sharing the changes under the same license: use other people’s work, and share yours!

This constraint does not propagate through APIs: You can use OSRD as a library, framework or API server to interface with proprietary software. Please suggest changes if you need new interfaces.

This chapter is about the process of integrating changes into the common code base. **If you need help at any stage, open an issue or message us.**

If you are not used to Git, follow this tutorial

**Create a branch**

If you intend to contribute regularly, you can request access to the main repository. Otherwise, create a fork.**Add changes to your branch**

Before you start working, try to split your work into macroscopic steps. At the end of each stop, save your changes into a commit. Try to make commits of logical and atomic units. Try to follow style conventions.**Keep your branch up-to-date**`git switch <your_branch> git fetch git rebase origin/dev`

**Open a pull request**

Once your changes are ready, you have to request integration with the`dev`

branch. If possible, make PR of logical and atomic units too (avoid mixing refactoring, new features and bug fix at the same time). Add a description to PRs to explain what they do and why. Help the reviewer by following advice given in mtlynch article. Add tags`Area:<affected_area>`

to show which part of the application have been impacted. It can be done through the web interface.**Take feedback into account**

Once your pull request is open, other contributors can review your changes:- Any user can review your changes
- Your code has to be approved by a contributor familiar with the code
- All users are expected to take comments into account.
- Comments tend to be written in an open and direct manner. The intent is to efficiently collaborate towards a solution we all agree on.
- Once all discussions are resolved, a maintainer integrates the change.

As a reviewer try to follow advice given in mtlynch article.

**If you believe somebody forgot to review / merge your change, please speak out, multiple times if needs be.**

## Git commit style

The overall format for git commits is as follows:

```
component: imperative description of the change
Detailed description of the change and what motivates it,
if it is not entirely obvious from the title.
```

**the commit message, just like the code, must be in english**- all lowercase
- there can be multiple components separated by
`:`

in case of hierarchical relationships, with`,`

otherwise - the body of the commit should probably contain a detailed description of the change

# 3.1.3 - Style guide

OSRD application is split in multiple services written in several languages. We try to follow general code best practices and follow specificity for each languages when required.

## General rules

- Explain what you’re doing and why.
- Document new code with doc comments.
- Include clear, simple tests.
- Break work into digestible chunks.
- Take the time to pick good names. Avoid non well-known abbreviations.
**Control and consistency over 3rd party code reuse**: Only add a dependency if it is absolutely necessary. Every dependency we add decreases our autonomy and consistency.**Don’t re-invent every wheel**: As a counter to the previous point, don’t re-invent everything at all costs. If there is a dependency in the ecosystem that is the “de-facto” standard, we should heavily consider using it.- More code general recommendations in main repository CONTRIBUTING.md.
- Ask for any help that you need!

### Python

Python code is used for some packages and integration testing.

- Follow the Zen of Python.
- Code is linted with flake8.
- Code is formatted with Black.
- Imports are sorted with Isort.
- Python tests are written using pytest.

### Python

- Use the documentation example to know how to phrase and format your documentation.
- Code is linted with clippy.
- Code is formatted with fmt.
- Rust code is tested files per files following these recommendations.

### Rust

- As a reference for our API development we are using the Rust API guidelines. Generally, these should be followed.
- Prefer granular imports over glob imports like
`diesel::*`

. - Tests are written with the built-in testing framework.
- Use the documentation example to know how to phrase and format your documentation.
- Use consistent comment style:
`///`

doc comments belong above`#[derive(Trait)]`

invocations.`//`

comments should generally go above the line in question, rather than in-line.- Start comments with capital letters. End them with a period if they are sentence-like.

- Use comments to organize long and complex stretches of code that can’t sensibly be refactored into separate functions.
- Code is linted with clippy.
- Code is formatted with fmt.

### Java

- Code is formatted with checkstyle.

### Javascript / Typescript / Front

- When adding new files, write them in TypeScript as there is a goal to move to TypeScript.
- Use generated endpoints from the
`openapi.yaml`

files to consume the backend. - Code is linted with eslint.
- Code is formatted with prettier.
- End-to-end tests are required for stable and critical features. Playwright is used to write these tests.
- To write unit test use vitest.

### Integration tests

- Integration tests are written using pytest under the
`/tests`

folder. - Each route described in the
`openapi.yaml`

files must have an integration test. - Test must check both the valid and invalid response format and content.

# 3.1.4 - Review code

# 4 - Technical reference

Technical reference guides contain technical reference for APIs and other aspects of OSRD’s machinery. They describe how it works and how to use it but assume that you have a basic understanding of key concepts.

# 4.1 - Architecture

It is a multi-service architecture where several software components interact with each other. This choice was made to ensure the modularity of the code and to guarantee the exploitability of certain OSRD services by external applications.

# 4.2 - Design documents

Design documents are meant to help understand and participate in designing software.

Each design document describes a number of things about a piece of software:

- its goals
- its constraints
- how its inputs and outputs were modeled
- how it works

# 4.2.1 - Conflict detection

This document is a work in progress

Conflict detection is a process which enables figuring out if a timetable, comprised of many train paths, can be performed as planned.

If a train has to slow down because of another train, or undergoing work on rail infrastructure, these actors are in conflict, which causes the timetable to become impossible to perform.

Conflict detection relies on interlocking and signaling simulation to:

- figure out what each actor requires to perform its duty undisturbed
- detect conflicting requirements

This system has to:

- produce conflicts which can be linked back to a root cause
- operate in way that can be visualized and interpreted
- be scalable: it should work just as fast given a massive number of train paths to intersect
- enable threading new train paths into an existing timetable

The design of this system is guided by a number of constraints:

**it must not cause false-negatives**: if no conflicts are detected, a multi-train simulation of the same timetable must not yield any slowdowns- it cannot rely on data we do not have
- it has to enable later support of mobile block systems
- it has to rely on existing signaling and interlocking simulation
- it has to enable detecting conflicts regardless of the signaling system in use
- it has to support transitions between signaling systems
- it has to support conflicts between different signaling systems

## Conflict theory

Conflict detection relies on an abstract model:

- objects where conflicts happen are resources
- resources are used by actors
- resource uses can be incompatible
- overlapping incompatible ressource uses generate a conflict

### Actors

Actors are objects which cause resources to be used:

**train paths**(or someone / something on the behalf of the train)**planned infrastructure work**

### Resources

Resources have the following properties:

- can be used by actors
- have multiple states
- can only be in a single state at any given time
- may take time changing state

The current model only considers the following two resources:

**zones**, which have one state per way to traverse it**switches**, which have one state per position

Station platforms could also be resources, which would enable modeling platform use conflicts: if two tracks share a tiny platform, it may not be allowed to use both sides of the platform at once.

### Requirements and conflicts

A **requirement** describes what resource is needed by an actor, and when.

There are a few types of requirements:

**shareable**: the resource is required to be in a given configuration, for a given time lapse. Other shareable requirements with the same configuration can stack up.**exclusive**: the resource is required to be in a given configuration, for a given actor, for a given time lapse. This requirement cannot be combined with others.

**Conflicts** are both real-life disturbances and the incompatible resource requirements which cause them. A disturbance is something that would cause a train to be unable to run as planned.

- route setup delays due to unavailable resources
- spacing conflicts due to a train catching up with another

These real-life issues can be linked to conflicts, which arise from incompatible resource requirements:

- an exclusive requirement overlaps another requirement
- a shareable requirements overlaps an incompatible requirement
- a resource does not have time to change state between non-overlapping, yet close, requirements

For example, there are **no** conflicts when:

- a resource has non-overlapping exclusive requirements
- a resource is shared by requirements which require the same configuration

## Generating requirements

**For conflict detection to work, resource requirements have to strictly match what’s required
to garantee that a simulated train will not be disturbed.**

### Routing requirements

For a train to be routed across rail infrastructure without any trouble, some resources have to be available for routes to set in time. A route does not set in time if a train is disturbed by a route being set too late.

A route might be set too late for a couple of reasons:

- one of the zones required may be unavailable
- a switch may be unavailable, or still moving

Routing requirements are generated by the following algorithm:

- compute a route calling timeline, so that the train never slows down due to late route setting. It can be done by finding when the driver would be constrained by signaling, and using that minus an allowance as the deadline.
- for each zone in each route, simulate when it would be released, and thus not required anymore
- use the route calling deadline as the requirement start time for each zone, and the release time as the requirement end. It is a shareable requirement. Switch requirements use the same time bounds.

Implementing route overlaps alternatives would require a richer model for requirements, such as a hierarchy and alternatives

### Spacing requirements

Trains can also be slowed down by catching up with another train. When this happens, signaling forces the following train to slow down, or even stop if the train being followed does not clear the way fast enough.

These slowdowns are conflicts, and thus need to be avoided by emitting sufficient requirements.

**At any time, exclusive requirements are emitted for zones which if occupied, would trigger a slowdown**

These requirements are emitted as follows:

- the route calling timeline is followed
- every time the driver sees a signal:
- start by assuming routes reserved for routing should be reserved for spacing as well
- until the driver sees a signal which would cause a slowdown:
- attempt to make the last reserved zone occupied by a virtual train
- if the signal now causes a slowdown, the currently reserved zones are correct, continue to the next signal
- if not, unreserve the last zone of the path

## Detecting conflicts

# 4.2.2 - Search for last-minute train slots (STDCM)

OSRD can be used to find a slot for a train in an already established timetable, without causing conflicts with other trains.

The acronym STDCM (Short Term Digital Capacity Management) is used to describe this concept in general.

# 4.2.2.1 - Business context

Some definitions:

### Capacity

**Capacity**, in this context, is the ability to
reserve infrastructure elements to allow the passage of a train.

Capacity is expressed in both space and time: the reservation of an element can block a specific zone that becomes inaccessible to other trains, and this reservation lasts for a given time interval.

It can be displayed on a chart, with the time on the horizontal axis and the distance traveled on the vertical axis.

Example of a space-time chart displaying the passage of a train.

The colors here represent aspects of the signals, but display a consumption of the capacity as well: when these blocks overlap for two trains, they conflict.

There is a **conflict** between two trains when they reserve
the same object at the same time, in incompatible configurations.

Example of a space-time graph with a conflict: the second train is faster than the first one, they are in conflict at the end of the path, when the rectangles overlap.

When simulating this timetable, the second train would be slowed down by the yellow signals, caused by the presence of the first train.

### Train slots

A **train slot** corresponds to a capacity reservation
for the passage of a train. It is fixed in space and time:
the departure time and the path taken are known.
On the space-time charts in this page, a train slot corresponds
to the set of blocks displayed for a train.

Note: in English-speaking countries, these are often simply called “train paths”. But in this context, this name would be ambiguous with the physical path taken by the train.

The usual procedure is for the infrastructure manager (e.g. SNCF Réseau) to offers train slots for sale to railway companies (e.g. SNCF Voyageurs).

At a given date before the scheduled day of operation,
all the train paths are allocated. But **there may be
enough capacity to fit more trains**. Trains can fit between
scheduled slots,
when they are sufficiently far apart or have not found a buyer.

The remaining capacity after the allocation of train paths is called
**residual capacity**. This section explains how OSRD looks
for train slots in this residual capacity.

# 4.2.2.2 - Train slot search module

This module handles the search for solutions.

To reduce the problem to its simplest form and for easy and efficient testing, inputs and outputs are strongly simplified and abstracted.

To summarize its behavior: the solution space is described as a graph that encodes locations, time, and speed. A pathfinding is run on this graph to find a solution.

This graph could, in a way, be seen as a decision tree, but different paths can lead to the same node.

# 4.2.2.2.1 - Input format

This module takes several parameters to find a path:

- A graph describing the physical infrastructure
- Unavailable sections in time intervals
- Origin and destination point(s)
- Departure time interval
- Maximum run time
- Simulation parameters (rolling stock, time step, allowances, …)

Among those, the first 3 require more explanations.

#### Infrastructure graph

Today, the input graph is the `SignalingRoutes`

graph.
But it can be any graph that represents the physical infrastructures
and the paths that can be used.

The only constraints are: the edges must have a length, and it must be possible to compute running time on parts of an edge.

#### Unavailable sections

This input encodes the areas that are unavailable because of capacity constraints.

Every edge has a set of “occupancy block”. A block is made of these elements:

- Start offset
- End offset
- Start time
- End time

Offsets are relative to the start of the edge. Each block means that
the *head* of the train cannot be located in the edge
segment during the given interval.

These blocks include the grid margin. If the solution needs to have
an `x`

seconds margin before the train passage, every block ends
`x`

seconds later.

To give an example, with the following schedule, a 42m long train, and 10m sight distance:

- The occupancy of the block 1 from t=0 to t=300 makes it unavailable in its entirety during this time
- The last 10 meters of block 1 are unavailable from t=300 to t=360, because the signal at the start of block 2 must be green when the conductor sees it. It is possible to consider that this unavailability block starts at t=130 (when the next signal isn’t green), as blocks can overlap.
- The occupancy of block 2 from t=130 to t=360 makes it unavailable during this time. It is also unavailable from t=0, as the presence of a train in this block would cause a warning on block 1.
- The first 42 meters of block 3 are unavailable from t=0 to t=360, because the tail of the train must have left the block 2 at this time.
- The rest of block 3 is unavailable in its entirety from t=280 to t=360

# 4.2.2.2.2 - Encoding the solution space

#### General principle

The problem is still a pathfinding problem in a given graph. Once the problem is encoded as a graph search, it is possible to reuse our existing tools for this purpose.

We consider the *product graph* of position, time, and speed.
This means that every graph element contains these 3 variables
(among other things)

Every graph edge is computed using running-time calculation to get speed and positions as functions of time.

#### Graphical representation

Space is encoded with a graph that contains the physical infrastructure.

It is then “duplicated” at different times.

The nodes are then linked together in a way that reflects travel time.

#### Notes

- The graph is constructed on the fly as it is explored.
- It is
*discretized*in time, to evaluate which nodes have already been visited. We keep full accuracy of time values, but two nodes at the same place and close times are considered identical. - Every edge is computed with a running time computation.
- Speed isn’t discretized or considered to check visited nodes, it’s only used to compute time.
- By default, the train always goes as fast as it can (while still following standard allowances). It only slows down when necessary.

#### Example

For example, with the following infrastructure, using the track graph:

Exploring the solution graph can give the following result:

# 4.2.2.2.3 - Discontinuities and backtracking

#### The discontinuity problem

When a new graph edge is visited, a simulation is run to evaluate its speed. But it is not possible to see beyond the current edge. This makes it difficult to compute braking curves, because they can span over several edges.

This example illustrates the problem: by default the first edge is explored by going at maximum speed. The destination is only visible once the second edge is visited, which doesn’t leave enough distance to stop.

#### Solution : backtracking

To solve this problem, when an edge is generated with a discontinuity in the speed envelopes, the algorithm goes back over the previous edges to create new ones that include the decelerations.

To give a simplified example, on a path of 4 edges where the train can accelerate or decelerate by 10km/h per edge:

For the train to stop at the end of route 4, it must be at most at 10km/h at the end of edge 3. A new edge is then created on edge 3, which ends at 10km/h. A deceleration is computed backwards from the end of the edge back to the start, until the original curve is met (or the start of the edge).

In this example, the discontinuity has only been moved to the transition between edges 2 and 3. The process is then repeated on edge 2, which gives the following result:

Old edges are still present in the graph as they can lead to other solutions.

# 4.2.2.2.4 - Conflict avoidance

While exploring the graph, it is possible to end up in locations that would generate conflicts. They can be avoided by adding delay.

#### Shifting the departure time

The departure time is defined as an interval in the module parameters:
the train can leave at a given time, or up to `x`

seconds later.
Whenever possible, delay should be added by shifting the departure time.

for example : a train can leave between 10:00 et 11:00. Leaving at 10:00 would cause a conflict, the train actually needs to enter the destination station 15 minutes later. Making the train leave at 10:15 solves the problem.

In OSRD, this feature is handled by keeping track, for every edge, of the maximum duration by which we can delay the departure time. As long as this value is enough, conflicts are avoided this way.

This time shift is a value stored in every edge of the path. Once a path is found, the value is summed over the whole path. This is added to the departure time.

For example :

- a train leaves between 10:00 and 11:00. The initial maximum time shift is 1:00.
- At some point, an edge becomes unavailable 20 minutes after the train passage. The value is now at 20 for any edge accessed from here.
- The departure time is then delayed by 5 minutes to avoid a conflict. The maximum time shift value is now at 15 minutes.
- This process is applied until the destination is found, or until no more delay can be added this way.

#### Engineering allowances

Once the maximum delay is at 0, the delay needs to be added between two points of the path.

The idea is the same as the one used to fix speed discontinuities: new edges are created, replacing the previous ones. The new edges have an engineering allowance, to add the delay where it is possible.

computing an engineering allowance is a feature of the running-time calculation module. It adds a given delay between two points of a path, without affecting the speeds on the rest of the path.

# 4.2.2.2.5 - Standard allowance

The STDCM module must be usable with standard allowances. The user can set an allowance value, expressed either as a function of the running time or the travelled distance. This time must be added to the running time, so that it arrives later compared to its fastest possible running time.

For example: the user can set a margin of 5 minutes per 100km. On a 42km long path that would take 10 minutes at best, the train should arrive 12 minutes and 6 seconds after leaving.

This can cause problems to detect conflicts, as an allowance would move the end of the train slot to a later time. The allowance must be considered when we compute conflicts as the graph is explored.

The allowance must also follow the MARECO model: the extra time isn’t added evenly over the whole path, it is computed in a way that requires knowing the whole path. This is done to optimize the energy used by the train.

#### Linear margin expressed as a function of time

As a first step, the problem is solved with a linear margin, i.e. added evenly over the whole path. The speed is simply modified by a constant factor.

The envelopes. computed during the graph traversal are not modified, they are always at maximum speed. But they are paired with a speed factor, which is used to compute running time and to evaluate conflicts.

The final envelope, with the allowance, is only computed once a path is found.

#### Linear margin expressed as a function of distance

The principle is generally the same, but with an extra difficulty: the speed factor isn’t constant over the path. When a train goes faster, it travels more distance in the same time, which increases the allowance time and the speed factor.

Because the train speed changes over the path, the speed factor changes from one edge to another. This causes irregular speed curves.

#### MARECO Allowances

This is exclusively a post-processing step, because it isn’t possible to compute the MARECO envelope without knowing the full train path. When looking for a path, linear allowances are used.

This means that conflicts may appear at this step. To avoid them, the following procedure is applied:

- A mareco allowance is applied over the whole path.
- If there are conflict, the first one is considered.
- The mareco allowance is
*split in two intervals*. The point where the first conflict appeared is set to be at the same time as the envelope with a linear allowance, removing the conflict at this point. - This process is repeated iteratively until no conflict is found.

# 4.2.2.2.6 - Implementation details

This page is about implementation details. It isn’t necessary to understand general principles, but it helps before reading the code.

#### STDCMEdgeBuilder

This refers to this class in the project.

This class is used to make it easier to create instances of
`STDCMEdge`

, the graph edges. Those contain many attributes,
most of which can be determined from the context (e.g. the
previous node).
The `STDCMEdgeBuilder`

class makes some parameters optional
and automatically computes others.

Once instantiated and parametrized, an `STDCMEdgeBuilder`

has two methods:

`Collection<STDCMEdge> makeAllEdges()`

can be used to create all the possible edges in the given context for a given route. If there are several “openings” between occupancy blocks, one edge is instantiated for each opening. Every conflict, their avoidance, and their related attributes are handled here.`STDCMEdge findEdgeSameNextOccupancy(double timeNextOccupancy)`

: This method is used to get the specific edges that uses a certain opening (when it exists), identified here with the time of the next occupancy block. It is called whenever a new edge must be re-created to replace an old one. It calls the previous method.

### Pathfinding

The methods mentioned here are defined in this class.

#### Cost function

The function used to define pathfinding cost sets which path is used over another. The result is always the one that minimizes this cost (as long as the heuristic is admissible).

Here, two parameters are used: total run time and departure time. The latter has a very small weight compared to the former, so that the fastest path is found. More details are explained in the documentation of those methods.

#### Heuristics

The algorithm used to find a path is an A*, with a heuristic based on geographical coordinates.

However, the coordinates of generated infrastructures are arbitrary and don’t reflect the track distance. It means that, for the generated infrastructures, the path may not always be the shortest one.

It would be possible to use this heuristic to determine whether the current node can lead to a path that doesn’t take longer than the maximum allowed total run time. But for the same reason, adding this feature would break any STDCM test on generated infras. More details in this issue.

# 4.2.2.3 - Signaling interface

WIP

There’s a draft of what we intend to do on the French page, but it’s still a work in progress. The implementation hasn’t been started.

# 4.3 - APIs

# 4.3.1 - JSON Schemas

# Infrastructure

# Rolling Stock

# 5 - Railway Wiki

This wiki is meant to help software engineers have a deep understanding of railway systems.

**It can only happen if content is added as needed. If something is missing, contribute!**

# 5.1 - Glossary

Please open an issue if you’re missing a word