Data Unit Contract
This document fixes the unit contract expected by the simulator. The goal is to avoid ambiguity when using hourly datasets, sub-hourly datasets or real data that originally arrives as power.
Base Rule
The physical engine accounts for energy per simulation step.
Quantity |
Unit |
|---|---|
Time-series energy columns |
|
Nominal power, limits, contracts and physical power actions |
|
Storage capacity |
|
State of charge ( |
fraction in |
Energy prices |
currency per |
Carbon intensity |
CO2 mass per |
seconds_per_time_step in the schema must represent the physical cadence of the dataset. If the dataset resolution and schema resolution differ, the simulator applies the compatibility conversion and prints an explicit [CityLearn][unit-conversion] warning. This is supported, but the preferred production path is to generate the dataset at the same resolution declared by the schema.
Main Time Series
Field |
Unit |
Notes |
|---|---|---|
|
|
Energy consumed in the step. |
|
|
Thermal demand in the step. |
|
|
Thermal demand in the step. |
|
|
Thermal demand in the step. |
|
PV profile per installed kW or |
Depends on |
|
currency/ |
Not scaled by step size. |
|
CO2/ |
Not scaled by step size. |
|
physical unit of each variable |
Temperature, irradiance and humidity are not energy. |
Equipment
Element |
Typical field |
Unit |
|---|---|---|
Battery / electrical storage |
|
|
Battery / electrical storage |
|
|
Thermal storage |
|
|
Thermal storage |
|
|
Heat pumps, heaters, chillers |
|
electrical |
PV |
|
|
EV charger |
|
|
Electric vehicle |
battery |
|
Deferrable appliance |
cycle |
|
Deferrable appliance |
cycle |
|
Grid/phase/contract limits |
power limits |
|
Any kW limit is converted to step energy with:
max_energy_kwh_step = limit_kw * seconds_per_time_step / 3600
Real Data Conversion
When the real source arrives as power, the converter must produce energy columns in kWh/step before generating the CityLearn dataset.
energy_kwh_step = mean_power_kw_over_step * seconds_per_time_step / 3600
Practical rules:
Source |
Conversion |
|---|---|
Power series in |
Aggregate to simulator cadence with time mean, then convert to |
Interval readings already in |
Sum intervals belonging to the simulation step. |
Cumulative |
Difference consecutive readings, then aggregate. |
Prices and emissions |
Align or forward-fill to simulator cadence; do not multiply by step size. |
Phase limits, contracted power and device ratings |
Keep in |
EV timestamps |
Convert absolute times to integer timesteps used by charger schedules. |
Deferrable timestamps |
Use global dataset timesteps for |
EV charger countdowns |
|
Entity derived forecasts |
Computed from future dataset values in simulator time, with horizons converted from seconds using |
Entity action feedback |
Requested/limited actions are normalized commands; requested/limited/applied power fields are |
Entity BESS energy capacity |
|
Sub-minute datasets |
Include optional |
Quality flags like |
Keep them in the ingestion/conversion pipeline; they are not native physical CityLearn fields. |
Example:
load_kw = 55.0
seconds_per_time_step = 15
load_kwh_step = 55.0 * 15 / 3600 = 0.2291667
Small kWh/step values are physically correct at 15s. If an agent needs kW or normalized values, convert in the agent observation layer, not in the internal energy balance.
Formats and Loading
Time-series files referenced by the schema may be CSV (.csv) or Parquet (.parquet, .pq, .parq). The column and unit contract is identical in both formats.
The loader reads only the window declared by simulation_start_time_step and simulation_end_time_step, while preserving the original offset so rolling or non-zero episodes stay aligned with global dataset indices.
For large datasets, especially full-year 15s data, Parquet is usually better than CSV because it preserves types, compresses better and avoids text parsing. Parquet support requires pyarrow; without it, use CSV.
When several buildings point to the same weather, pricing or carbon_intensity file and noise_std = 0, the simulator shares the same in-memory time series. energy_simulation remains building-specific.
Deferrable Appliances
The official format for deferrable loads uses two files per appliance:
File |
Purpose |
|---|---|
|
Physical cycle profile catalog. |
|
Flexibility requests pointing to catalog entries through |
cycle_profiles_file:
Field |
Unit/type |
Notes |
|---|---|---|
|
identifier |
Stable physical profile key. |
|
steps |
Must match |
|
|
Must match |
|
list of |
Energy consumed at each cycle step. |
flexibility_schedule_file:
Field |
Unit/type |
Notes |
|---|---|---|
|
identifier |
Unique request/occurrence. |
|
identifier |
Reference to the catalog. |
|
global timestep |
First timestep where the cycle may start. |
|
global timestep |
Last timestep where the agent may start. |
|
global timestep |
Last timestep by which the cycle must be complete. |
|
ratio |
External request priority/urgency. |
|
bool |
Service contract flag. |
The RL action is simple: start. If the action exceeds the threshold, the simulator tries to start the next pending cycle. The cycle starts only inside [earliest_start_time_step, latest_start_time_step] and only if it fits before deadline_time_step. If latest_start_time_step passes without a valid start, the cycle is marked missed.
If real cycle data arrives as power (kW), the converter must generate load_profile as energy:
cycle_step_kwh = cycle_step_kw * seconds_per_time_step / 3600
Do not repeat load_profile per timestep in the dataset. Temporal repetition belongs in flexibility_schedule_file, pointing to the same profile_id.
EV Flexibility Features
In the entity interface, EV flexibility should preferably be consumed through physical/normalized derived features:
Feature |
Meaning |
|---|---|
|
Physical time until departure. |
|
|
|
Energy still required to reach target SOC. |
|
Average power required until departure. |
|
Margin between charger max power and required average power. |
|
Urgency in |
|
Energy that can still be delivered by departure under current feasible charge power. |
|
|
|
|
|
Required average charger power divided by charger max charging power; values above |
PV
energy_simulation.solar_generation is the easiest field to misinterpret. The simulator reads this field from the dataset. For compatibility with original CityLearn, the default mode treats it as generation profile per 1 kW of installed PV and multiplies by pv.nominal_power.
Default per_kw mode:
pv_generation_kwh_step = pv.nominal_power_kw * solar_generation / 1000
For real absolute data, declare:
"pv": {
"type": "citylearn.energy_model.PV",
"autosize": false,
"attributes": {
"nominal_power": 120.0,
"generation_mode": "absolute"
}
}
With generation_mode = "absolute", energy_simulation.solar_generation is interpreted directly as real PV energy in kWh/step:
pv_generation_kwh_step = solar_generation
In absolute mode, pv.nominal_power remains useful as installed power or physical rating in kW, but it no longer scales the dataset series.