Track Event Configuration

Overview

Track cycling events use the same entity hierarchy as all EMS events, but leverage the full depth of the model — particularly Rounds, StartGroups, and RaceTypeConfig — which simpler road events often leave unused.

This document describes how track events map to the EMS entity model, using the Match Sprint and Individual Time Trial as primary examples.

Entity Hierarchy

The core entities involved in configuring a track event are:

Event
└── EventCategory            -- e.g. "Elite Men", "Junior Women"
    └── Race                 -- one per EventCategory per discipline
        ├── RaceTypeConfig   -- defines the round/heat structure variation
        ├── Round            -- e.g. Qualifying, Quarter Finals, Finals
        │   ├── RoundType    -- template defining round rules and sequence
        │   ├── parent       -- self-referential for child rounds (e.g. Rides under a Final)
        │   └── prevRound    -- links to the preceding round in progression
        └── ProgramEntry     -- scheduled item on the event programme

StartGroup                   -- references one or more Races, optionally a Round
├── Race *                   -- a StartGroup can be shared across multiple Races
├── Round (optional)
└── StartGroupParticipant    -- assigns an EventParticipant to the StartGroup

ProgramEntry                 -- a scheduled item on the event programme
├── Event                    -- the event this entry belongs to
├── next / prev              -- chained to other ProgramEntries for multi-round races
└── StartGroup *             -- StartGroups reference the ProgramEntry (FK on StartGroup)

Participants Are Visible Through StartGroups

Neither Race nor Round directly owns participants. StartGroup is the entity through which a Race or Round reflects its participants. StartGroupParticipants assign individual EventParticipants to StartGroups, and StartGroups reference their Race(s) and optionally a Round.

This means:

  • A Race sees its participants as the union of all StartGroupParticipants across all of its StartGroups — spanning every round, every heat.

  • A Round sees its participants as the StartGroupParticipants in the StartGroups that reference that Round.

  • In a Match Sprint, the Qualifying heats, Quarter Final heats, Semi Final heats, and Final heats are all StartGroups of the single Match Sprint Race — differentiated by their Round reference.

Shared StartGroups Across Races

A StartGroup can reference multiple Races. This is used when a Race has too few participants to justify running it as a separate heat — for example, a category with only one entry. In these cases, participants from multiple Races are combined into a single StartGroup so they ride together.

When Races share a StartGroup:

  • Positions are calculated per Race — each Race determines its own finishing order from the participants within the shared StartGroup that belong to it.

  • Awards are assigned per Race — Gold, Silver, Bronze (or 1st, 2nd, 3rd) are always awarded within the context of a Race, not across the combined StartGroup.

  • The shared StartGroup is simply an operational grouping for the physical start; it does not merge the competitive classification.

This pattern is common in track events with small fields, where running a single rider alone would be impractical.

Example: Shared StartGroup

An event has an "Elite Men 1500m" Race with 4 entries and a "Masters Men 1500m" Race with 1 entry. Rather than having the Masters rider ride alone, both Races share a single StartGroup. All 5 riders start together, but positions and awards are calculated separately: the Elite race awards 1st–4th from its 4 riders, and the Masters race awards 1st from its single rider.

Key Relationships

Entity Role in Track Events

Event

The track meet itself (e.g. "SA Track Championships 2026").

EventCategory

Age/gender grouping (e.g. "Elite Men", "Junior Women"). Each EventParticipant belongs to one EventCategory.

Race

A specific discipline within an EventCategory (e.g. "Elite Men Match Sprint"). Links to an EventCategory, an EventRaceType (the discipline), a Course (the track), and optionally a RaceTypeConfig. The Race is the single container for all its StartGroups across all rounds. A Race is also the unit by which positions and awards (Gold, Silver, Bronze) are determined — even when its StartGroups are shared with other Races.

RaceTypeConfig

Defines the structural variation of a race based on entry count or competition level (World, National, Regional). Determines how many rounds and heats are created. Linked to a RaceType and selected per Race.

RoundType

Template for a round — defines the name, sequence, qualification rules, and parent structure. Belongs to a RaceType and optionally to a RaceTypeConfig. RoundTypes with a parent create nested structures (e.g. individual Rides within a Finals round).

Round

An instance of a RoundType within a specific Race. Carries scheduling (startTime), entry limits (minEntries, maxEntries), distance overrides, and lifecycle status. Rounds link to their Race, their RoundType, an optional parent Round, and an optional prevRound for progression sequencing. A Round does not directly own participants — it reflects them through the StartGroups that reference it.

StartGroup

Represents a heat. A StartGroup can reference one or more Races and optionally a Round. All StartGroups across all rounds of a discipline reference the same Race. A StartGroup may also be shared across multiple Races when fields are too small to run separately. In track events, a StartGroup typically contains 1–3 riders (solo time trial, 2-up sprint, 3-up sprint). May link to a ProgramEntry for scheduling.

StartGroupParticipant

Maps an EventParticipant into a specific StartGroup (heat). This is the entity that tracks a rider’s assignment to a particular heat. By belonging to a StartGroup, the participant is implicitly associated with both the Race and (if set) the Round.

ProgramEntry

A scheduled item on the event programme. Has a name, type, sequence, session number (for multi-day events), date/time, and event reference number. Belongs to an Event. StartGroups reference their ProgramEntry (the FK is on StartGroup), so a ProgramEntry reflects its heats through its collection of StartGroups. ProgramEntries can be chained via next/prev for multi-round races.

Match Sprint Configuration

The Match Sprint is the most complex track event to configure, using the full depth of the entity model.

RaceType and RaceTypeConfig

The RaceType "Match Sprint" has one or more RaceTypeConfig records that define the round structure based on entry count. For example:

RaceTypeConfig minEntries maxEntries Rounds Created

Sprint — 8 riders

5

8

Qualifying → Quarter Finals → Semi Finals → Finals

Sprint — 16 riders

9

16

Qualifying → 1/8 Finals → Quarter Finals → Semi Finals → Finals

Sprint — 32 riders

17

32

Qualifying → 1/16 Finals → 1/8 Finals → Quarter Finals → Semi Finals → Finals

The appropriate RaceTypeConfig is selected based on the number of entries and assigned to the Race.

UCI Terminology and EMS Entity Mapping

UCI Match Sprint terminology differs from EMS entity names. For the full mapping, see UCI to EMS Terminology Mapping. The key difference:

  • A UCI Race (an individual ride within a best-of-three Heat) is implemented as a Round in the EMS

  • A UCI Heat (a matchup between specific riders) is represented by StartGroupParticipant.seq within a Round’s StartGroup

  • The EMS Race entity represents the entire discipline for an EventCategory (e.g. "Elite Men Match Sprint")

RoundType Structure

Each RaceTypeConfig has associated RoundTypes that define the template for Round creation. A UCI Race (individual ride in a best-of-three) maps to a child RoundType under its parent Round. The RoundType hierarchy for a full Match Sprint might be:

RoundType: Qualifying            (seq=1)
RoundType: 1/16 Finals           (seq=2)
RoundType: 1/8 Finals            (seq=3)
RoundType: Quarter Finals        (seq=4)
├── RoundType: Race 1            (seq=1, parent=Quarter Finals)
├── RoundType: Race 2            (seq=2, parent=Quarter Finals)
└── RoundType: Decider           (seq=3, parent=Quarter Finals)
RoundType: Semi Finals           (seq=5)
├── RoundType: Race 1            (seq=1, parent=Semi Finals)
├── RoundType: Race 2            (seq=2, parent=Semi Finals)
└── RoundType: Decider           (seq=3, parent=Semi Finals)
RoundType: Finals                (seq=6)
├── RoundType: Race 1            (seq=1, parent=Finals)
├── RoundType: Race 2            (seq=2, parent=Finals)
└── RoundType: Decider           (seq=3, parent=Finals)

The qualificationRule on RoundType describes how participants progress from the previous Round (e.g. "Top 8 by time", "Winner of each Heat").

Round and StartGroup Instantiation

When a Race is created with a RaceTypeConfig, Rounds are instantiated from the RoundTypes. Each child RoundType (representing a UCI Race) becomes a Round under the EMS Race entity. The Round.seq field sequences all Rounds within the Race. Each Round has a single StartGroup (linked to the Race, Round, and a ProgramEntry) that contains all participants for that UCI Race, with StartGroupParticipant.seq distinguishing which Heat a rider belongs to.

Qualifying Round

  • A single Round is created from the "Qualifying" RoundType.

  • One StartGroup is created for the Round, referencing the Race and the Qualifying Round.

  • All EventParticipants are assigned as StartGroupParticipants — each rider rides alone (a flying 200m time trial), so each StartGroupParticipant effectively has its own seq.

  • Results record the final time plus optional split times (100m, 100m–200m) as result meta data.

  • Participants are ranked by time; the top qualifiers are marked and progress to the next Round.

Single-Race Rounds (1/16 Finals, 1/8 Finals)

  • One Round is created for the elimination stage.

  • A single StartGroup contains all participants, with StartGroupParticipant.seq grouping riders into Heats — typically 2-up (two riders per Heat), though 3-up Heats may be used with odd numbers.

  • The winner of each Heat advances to the next Round.

  • Each Round appears on the programme as a ProgramEntry.

Best-of-Three Rounds (Quarter Finals, Semi Finals, Finals)

  • A parent Round exists as a logical grouping (from the parent RoundType).

  • Child Rounds are created for each UCI Race: Race 1, Race 2, and Decider — each with its own seq value.

  • Each child Round has a single StartGroup containing all participants for that Race, with StartGroupParticipant.seq distinguishing the Heats within it.

  • The Finals contain Heats for both the gold medal race and the bronze medal race.

  • Child Rounds (UCI Races) each have their own ProgramEntry, as they are scheduled at different times in the programme.

Example: 4–7 Rider Match Sprint (Straight to Semi Finals)

For a Match Sprint with 4–7 entries (Qualifying → Semi Finals → Finals), all Rounds belong to the single "Elite Men Match Sprint" Race entity. The Round.seq sequences the Rounds across the full competition:

Round seq Parent Heats in StartGroup

Qualifying

1

1 per rider (solo time trial)

Semi Final — Race 1

1

Semi Finals

2 Heats (Seed 1 vs 4, Seed 2 vs 3)

Semi Final — Race 2

2

Semi Finals

2 Heats (same matchups)

Semi Final — Decider

3

Semi Finals

Up to 2 Heats (only if needed)

Final — Race 1

4

Finals

2 Heats (gold + bronze)

Final — Race 2

5

Finals

2 Heats

Final — Decider

6

Finals

Up to 2 Heats (only if needed)

Each Round has one StartGroup. Within each StartGroup, StartGroupParticipant.seq groups riders into their Heats. For example, Semi Final Race 1 has one StartGroup with 4 riders: seq 1 identifies Heat 1 (Seed 1 vs Seed 4), seq 2 identifies Heat 2 (Seed 2 vs Seed 3).

Example: 8-Rider Match Sprint (Full Bracket)

For an 8-rider Match Sprint (Qualifying → Quarter Finals → Semi Finals → Finals):

Round seq Parent Heats in StartGroup

Qualifying

1

1 per rider

Quarter Final — Race 1

1

Quarter Finals

4 Heats (1v8, 2v7, 3v6, 4v5)

Quarter Final — Race 2

2

Quarter Finals

4 Heats

Quarter Final — Decider

3

Quarter Finals

Up to 4 Heats

Semi Final — Race 1

4

Semi Finals

2 Heats

Semi Final — Race 2

5

Semi Finals

2 Heats

Semi Final — Decider

6

Semi Finals

Up to 2 Heats

Final — Race 1

7

Finals

2 Heats (gold + bronze)

Final — Race 2

8

Finals

2 Heats

Final — Decider

9

Finals

Up to 2 Heats

Progression

Participant progression between Rounds is managed by updating StartGroupParticipant records in the next Round’s StartGroup, based on results from the current Round. The prevRound relationship on Round establishes the progression chain. The participant’s association with the Race (EMS entity) remains constant — only the StartGroup (and therefore Round) changes as they advance through the Heats.

Programme

Rounds that appear on the event programme are linked to ProgramEntry records. The ProgramEntry provides scheduling (date/time, session for multi-day events) and sequencing (seq for programme order). ProgramEntries can be chained via next/prev links for multi-round races, allowing the programme to show the full progression of a discipline.

Individual Time Trial (ITT)

The ITT is simpler, using a flat round structure.

Configuration

  • A single Race is created per EventCategory.

  • No RaceTypeConfig is typically needed (no round progression).

  • A single Round may be created, or StartGroups may be linked directly to the Race.

StartGroups

  • For track ITT: riders start in pairs on opposite sides of the track. Each pair is assigned to a StartGroup with a specific start time. The StartGroup defines the common start time for the pair.

  • For road ITT: each rider starts individually, with a StartGroup per rider.

  • StartGroupParticipants link each EventParticipant to their assigned StartGroup.

Results

All StartGroupParticipants across the Race are combined and ranked by time for the final classification. Unlike the Match Sprint, there is no round progression — the single set of times produces the result.

Simple Event Configuration (Road Race)

For comparison, a simple road race uses a minimal subset of the same entities:

  • One Race per EventCategory.

  • One StartGroup per Race (all participants start together). In more advanced configurations, multiple Races may share a single StartGroup — see Shared StartGroups Across Races.

  • No Rounds.

  • StartGroupParticipants may be inferred rather than persisted, since every EventParticipant in the category starts together.

  • One ProgramEntry per Race.

This is the baseline configuration. Track events extend it by adding Rounds, multiple StartGroups per Round, RaceTypeConfig for structural variation, and participant progression between rounds.