This repository contains the experimental code and reproducibility workflow for the paper “Scheduling Tasks towards Energy Autarky: Benefits and Computational Costs of Flexibility.”
The paper is available at https://arxiv.org/abs/2607.03260 .
@misc{bredereck2026scheduling,
title = {Scheduling Tasks towards Energy Autarky: Benefits and Computational Costs of Flexibility},
author = {Robert Bredereck and Till Fluschnik and Klaus Heeger},
year = {2026},
eprint = {2607.03260},
archivePrefix = {arXiv},
primaryClass = {cs.DM}
}Create and activate a Python environment, then install the core dependencies from the repository root:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txtThe optimization model uses the Gurobi Optimizer through gurobipy. Running the experiments therefore also requires a compatible Gurobi installation and a valid Gurobi license. For the paper's experiments, Gurobi 12.0 (Python interface) was used.
The forecast archive is included at:
zipfiles/rad-data.zip
Prepare the experiment directory from experiments/:
cd experiments
python3 setup.pyThe setup script extracts the forecast data and creates the directory trees under:
data/
dicts/
pics/
results/
summaries/
The experiment scripts are intended to be run from experiments/. The cleaned scripts also resolve their own location where appropriate so that path handling is less fragile.
The SMARTENERGY.KOM source data and the derived job dictionary are not distributed with this repository. Before running the optimization experiments, obtain the required deployment-1 source data and generate the job dictionary as described in Generating the job dictionary.
The forecast data under:
zipfiles/rad-data.zip
was provided by the Institute for Electrical Information Technology at Clausthal University of Technology, Germany, https://www.iei.tu-clausthal.de/wetter/.
After extraction, the yearly forecast files are located at:
experiments/data/mean-rad-<year>.csv
Forecast files are included for 2016 through 2022. They provide minute-level month, day, hour, minute, radiation, and sun-angle values used to construct the photovoltaic output forecasts.
The appliance-level job dictionary is derived from the SMARTENERGY.KOM deployments described in:
- Alaa Alhamoud et al. “SMARTENERGY.KOM: An Intelligent System for Energy Saving in Smart Home.” Proceedings of the 3rd Workshop on GlObal Trends in Smart Cities, 2014, pp. 685–692.
- Alaa Alhamoud et al. “Extracting Human Behavior Patterns from Appliance-level Power Consumption Data.” Proceedings of the 12th European Conference on Wireless Sensor Networks, 2015, pp. 52–67.
The SMARTENERGY.KOM source data and the job dictionary derived from it are not redistributed with this repository. To reproduce the job dictionary used in the paper, the required deployment-1 data must be requested from the corresponding authors of the referenced SMARTENERGY.KOM publications, subject to the applicable data-access terms.
The required source data is not included in this repository. Request the SMARTENERGY.KOM deployment-1 data from the corresponding authors of the two publications cited above.
From the obtained data, extract or copy the deployment-1 files:
SensorEventHistory.csv
Sensors.csv
into the following directory structure:
experiments/
└── Raw_Data/
└── Deployment_1/
├── SensorEventHistory.csv
└── Sensors.csv
The script:
experiments/smartenergy.py
recreates the job dictionary used by the paper:
experiments/dicts/jdd_1_6_120.py
The generated dictionary is required before solve_instance.py or solve_all_instances.sh can reproduce the paper experiments.
The script uses the following fixed preprocessing parameters:
- deployment
1; - activity threshold
6W; - maximum inactivity gap
120seconds.
To run it, place the original SMARTENERGY.KOM deployment-1 files at:
experiments/Raw_Data/Deployment_1/SensorEventHistory.csv
experiments/Raw_Data/Deployment_1/Sensors.csv
Then run the following command from the experiments/ directory:
python3 smartenergy.pyThe script requires pandas, which is included in the core Python requirements.
The generated dictionary is written to:
experiments/dicts/jdd_1_6_120.py
The dictionary contains the jobs extracted from the source sensor data, including each job's duration, power value, start time, and end time. When the original source files match those used for the paper, the generated dictionary should contain 7,052 entries. A different number of entries indicates that the supplied source data or preprocessing inputs differ from those used for the paper.
To overwrite an existing dictionary explicitly, run:
python3 smartenergy.py --forceThe three values passed through --dict_days identify the preprocessing parameters used to construct a job dictionary.
For the paper experiments:
--dict_days 1 6 120selects:
experiments/dicts/jdd_1_6_120.py
and the dictionary:
JDD_1_6_120
The parameters mean:
1: the source deployment from which the jobs were derived;6: the activity threshold in watts, meaning that a device is considered active at measured power values of strictly greater than 6 W; and120: the maximum inactivity gap in seconds used when grouping activity periods into one job.
For example, if a kettle is active at 06:00:00, briefly inactive, and active again at 06:01:59, the active periods are treated as part of the same job because they are at most 120 seconds apart.
Each dictionary entry contains:
[[duration_minutes, power_watts],
[start_month, start_day, start_hour, start_minute],
[end_month, end_day, end_hour, end_minute]]
power_watts is the maximum recorded sensor value within the corresponding activity session, following the preprocessing procedure used to generate the paper dictionary.
Once generated from the required SMARTENERGY.KOM source data with the fixed preprocessing parameters above, the dictionary is treated as a fixed input to the reported experiments. Its entries must not be manually modified, including irregularities inherited from the measurements and preprocessing procedure, because doing so would change the paper instances.
Jobs are represented on the minute-level experiment grid by a release index, due index, processing length, per-minute power requirement, and informational appliance label. Only jobs whose release and due timestamps occur within the selected forecast interval are added to an instance.
With --relax 0, a job's due time is set to the end of its uninterrupted processing interval. For a positive relaxation value, the original release-to-due interval from the dictionary is extended before the release time and after the due time, subject to the experiment boundaries.
The experiments use the following photovoltaic configurations. All panels use a radiation threshold of 10 W/m² and an efficiency factor of 0.2.
| Identifier | Panel configuration |
|---|---|
RA |
48 m² facing south |
RB |
24 m² facing south and 24 m² facing west |
RC |
16 m² facing east, 16 m² facing south, and 16 m² facing west |
BS |
3 m² facing south |
BE |
3 m² facing east |
BW |
3 m² facing west |
Panel orientation is represented using a nonnegative cosine coefficient based on the difference between the panel direction and the recorded sun angle.
The paper experiments use:
- a battery capacity of 60,000 Wmin, corresponding to 1 kWh;
- an initial charge of 10% of capacity;
- a full-charge duration of 180 minutes; and
- equal input and output efficiency settings selected through
--loss.
The optional --rounding DIGITS argument rounds the inverse output-efficiency factor. It is not used by the complete paper experiment script.
The model is implemented using the Gurobi Optimizer through gurobipy.
The complete paper batch:
- minimizes total external energy with
--goal external_sum; - uses the unweighted objective;
- schedules every job non-preemptively within its release-to-due interval; and
- stores a result only after Gurobi reports an optimal solution and the solution passes verification.
The repository also contains additional optimization objectives and an older model branch. These variants were not used by solve_all_instances.sh and should be regarded as experimental.
Before running an experiment, ensure that experiments/dicts/jdd_1_6_120.py has been generated as described above.
From experiments/, run for example:
python3 solve_instance.py \
--year 2022 \
--start 3 5 4 0 \
--end 3 6 3 59 \
--pv_type BS \
--dict_days 1 6 120 \
--goal external_sum \
--loss 0.9 \
--relax 0.0 \
--battcap 60000The paper experiments use the external_sum objective, which minimizes the sum of minute-level external-power values over the selected interval.
Result files are written under:
results/<goal>/<pv_type>/
Existing result files are skipped unless --force is supplied. Add --plot to create an individual solution plot.
After repository setup and generation of dicts/jdd_1_6_120.py, run from experiments/:
bash solve_all_instances.shThe script executes 48,048 solver calls sequentially over the years, selected operational days, photovoltaic configurations, efficiency settings, and flexibility values used in the paper. This is a substantial computation. It can take days or even weeks.
The batch script checks that the extracted data/ directory exists. It does not run setup automatically.
Unweighted ILP results use:
<instance-id>_ilp.json
Experimental weighted results use:
<instance-id>_ilp-w.json
Each JSON file records:
- the instance identifier;
- the selected start time of each scheduled job;
- the battery state at each timestep;
- external power at each timestep;
- model-construction time;
- solver time;
- total running time; and
- the solver identifier.
Verification reconstructs the experiment instance and checks the job windows, schedule completeness, battery bounds, charging limit, battery-energy balance, nonnegative external power, and required final battery level.
The battery model permits photovoltaic surplus to be curtailed. A valid solver trajectory therefore does not have to coincide with a greedy simulation that stores every available unit of surplus.
The runtime-versus-irregularity figures require an additional preprocessing step after the experiment results have been generated.
Install the plot dependencies from the repository root:
python3 -m pip install -r requirements-plots.txtThen run from experiments/:
python3 precompute_clear_sky.py(Note that this takes several minutes or even few hours.)
The script processes the RA and BS results and creates:
data/external_sum/RA/irregularity_ssd.csv
data/external_sum/BS/irregularity_ssd.csv
For each experiment window, clear-sky global horizontal irradiance is computed with the Ineichen model using:
- latitude
51.80586; - longitude
10.35609; - timezone
Europe/Berlin; and - fixed reference year
2020.
The clear-sky radiation is transformed with the same photovoltaic configuration as the measured forecast. The normalized output is clipped to [0, 1.5]. Forecast irregularity is the sum of squared differences between consecutive normalized values.
With the complete batch, each generated CSV contains 616 unique experiment windows.
The fixed location, reference year, normalization bounds, and irregularity definition are part of the paper's plotting pipeline and are retained for reproduction.
After solving the complete experiment batch and computing the irregularity tables, run from experiments/:
python3 paper_plots.py(Note that this takes several minutes.) The script recreates all figures gathered by the paper plotting workflow. Figures are written under:
pics/
LaTeX tables and statistical summaries are written under:
summaries/
The paper plotting style uses LaTeX-rendered text, so a working LaTeX installation is required in addition to the packages in requirements-plots.txt.
Bootstrap confidence intervals use a fixed random seed so that repeated executions generate the same intervals.
The general-purpose individual-solution plot remains available separately in:
experiments/plotter.py
The repository retains an experimental objective that incorporates electricity prices. It was not used for the paper experiments and is not required to reproduce the reported results.
Electricity-price data is not distributed with this repository. The weighted objective requires a user-provided CSV containing:
Datetime (Local)
Price (EUR/MWhe)
Datetime values must use:
YYYY-MM-DD HH:MM:SS
An example invocation is:
python3 solve_instance.py \
--weighted \
--price-file /path/to/electricity-prices.csv \
--year 2022 \
--start 3 5 4 0 \
--end 3 6 3 59 \
--pv_type BS \
--dict_days 1 6 120 \
--goal external_sum \
--loss 0.9 \
--relax 0.0 \
--battcap 60000Lower-frequency prices are converted to minute-level values by forward filling. The weighted implementation is retained as experimental functionality and is not called by solve_all_instances.sh.
The source code in this repository is licensed under the MIT License. See the root-level LICENSE file for details.
The MIT License applies only to the software authored for this repository. It does not automatically apply to third-party or derived data included in or referenced by the repository.
In particular:
- the forecast archive under
zipfiles/rad-data.zipwas provided by the Institute for Electrical Information Technology at Clausthal University of Technology and may be subject to separate terms; - the SMARTENERGY.KOM source data is not distributed with this repository and must be obtained separately from the corresponding authors of the cited publications under the applicable terms. The job dictionary generated from that data is likewise not distributed with this repository.
The smartenergy.py preprocessing script is part of the source code of this repository and is covered by the MIT License. The MIT License does not grant rights to the SMARTENERGY.KOM source data or to derived data products.
Users are responsible for complying with any applicable terms governing these data sources.