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.
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.seqwithin 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.seqgrouping 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
seqvalue. -
Each child Round has a single StartGroup containing all participants for that Race, with
StartGroupParticipant.seqdistinguishing 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.
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.