|
| 1 | +<p align="center"> |
| 2 | + <img src="images/logo.png" alt="load-shift-optimizer logo" width="256px" > |
| 3 | +</p> |
| 4 | + |
| 5 | +<h2 align="center"> Shift loads optimally to minimize electricity costs </h2> |
| 6 | + |
| 7 | +<div align="center"> |
| 8 | + |
| 9 | + |
| 10 | +[](https://github.com/NoviaIntSysGroup/load-shift-optimizer/actions/workflows/lint.yml) |
| 11 | +[](https://github.com/NoviaIntSysGroup/load-shift-optimizer/actions/workflows/tests.yml) |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +</div> |
| 18 | + |
| 19 | +<div align="center"> |
| 20 | + |
| 21 | +`loadshift` helps you optimally shift loads based on known prices and a given flexibility level, where the flexibility level indicates how many hours earlier or later a load can run. The package can be used to determine optimal load shifts based on day-ahead electricity prices, or to evaluate potential savings from various load-shifting scenarios. |
| 22 | + |
| 23 | +</div> |
| 24 | + |
| 25 | +## Features |
| 26 | + |
| 27 | +* ⚡ **Cost Optimization** - Shift loads optimally to minimize costs based on known electricity prices. |
| 28 | +* 🎛️ **Flexible Constraints** - Define how many hours loads can shift earlier or later, transfer rate limits, and power capacity to match your use case. |
| 29 | +* 📅 **Moving Horizon** - Daily optimization approach that replicates real-world day-ahead market scenarios. |
| 30 | + |
| 31 | +## Example |
| 32 | +The example below shows how electricity prices and residential loads change over a typical day: prices peak in the morning and evening, while residential loads peak in the evening when people get home. The two rightmost panels illustrate the results of shifting loads optimally for two flexibility levels: ±2 hours and ±4 hours. Observe how more and more consumption shifts towards nighttime and the afternoon as the flexibility level increases. |
| 33 | + |
| 34 | + |
| 35 | +<p align="center"> |
| 36 | +<img src="examples/load_shift_example.png" style="max-width: 600px; width: 100%; height: auto;" /> |
| 37 | +</p> |
| 38 | + |
| 39 | +## Installation |
| 40 | + |
| 41 | +### From PyPI (recommended) |
| 42 | + |
| 43 | +To use `load-shift-optimizer` in your project, install it from PyPI: |
| 44 | + |
| 45 | +```bash |
| 46 | +pip install loadshift |
| 47 | +``` |
| 48 | + |
| 49 | +By default, this installs the free open-source MIP solver (CBC backend). For better performance on large problems, you can install with Gurobi support if you have a Gurobi license: |
| 50 | + |
| 51 | +```bash |
| 52 | +pip install loadshift[gurobi] |
| 53 | +``` |
| 54 | + |
| 55 | +### From source (for examples and development) |
| 56 | + |
| 57 | +If you want to explore the examples or contribute to the project, follow these steps to install from source: |
| 58 | + |
| 59 | +```bash |
| 60 | +# clone repository |
| 61 | +$ git clone https://github.com/NoviaIntSysGroup/load-shift-optimizer.git |
| 62 | +$ cd load-shift-optimizer |
| 63 | + |
| 64 | +# install package and development dependencies (with MIP solver) |
| 65 | +$ uv sync |
| 66 | + |
| 67 | +# OR install with Gurobi support (requires a license) |
| 68 | +$ uv sync --extra gurobi |
| 69 | +``` |
| 70 | + |
| 71 | +## Usage |
| 72 | + |
| 73 | +### Basic Optimization |
| 74 | + |
| 75 | +```python |
| 76 | +import numpy as np |
| 77 | +from loadshift import LoadShifter |
| 78 | + |
| 79 | +# Define your price and demand data |
| 80 | +price = np.array([30, 80, 20, 40, 35, 25]) # ct/kWh |
| 81 | +demand = np.array([10, 15, 8, 12, 10, 9]) # kWh |
| 82 | + |
| 83 | +# Create optimizer with flexibility constraints |
| 84 | +optimizer = LoadShifter( |
| 85 | + max_demand_advance=2, # Can shift loads up to 2 hours earlier |
| 86 | + max_demand_delay=3, # Can shift loads up to 3 hours later |
| 87 | + max_hourly_purchase=20, # Maximum 20 kWh per hour |
| 88 | + max_rate=10 # Maximum 10 kW transfer rate |
| 89 | +) |
| 90 | + |
| 91 | +# Optimize demand |
| 92 | +result = optimizer.optimize_demand(price, demand) |
| 93 | + |
| 94 | +print("Optimal demand:", result["optimal_demand"]) |
| 95 | +print("Demand shift:", result["optimal_shift"]) |
| 96 | +``` |
| 97 | + |
| 98 | +### Moving Horizon Optimization |
| 99 | + |
| 100 | +```python |
| 101 | +import pandas as pd |
| 102 | +from loadshift import moving_horizon |
| 103 | + |
| 104 | +# Create DataFrames with a datetime index |
| 105 | +index = pd.date_range("2024-01-01", periods=72, freq="h") |
| 106 | +price_data = pd.DataFrame({"price": price_values}, index=index) |
| 107 | +demand_data = pd.DataFrame({"demand": demand_values}, index=index) |
| 108 | + |
| 109 | +# Configuration |
| 110 | +config = { |
| 111 | + "daily_decision_hour": 12, # Make decisions at noon each day |
| 112 | + "n_lookahead_hours": 36, # Look ahead 36 hours |
| 113 | + "load_shift": { |
| 114 | + "max_demand_advance": 2, |
| 115 | + "max_demand_delay": 3, |
| 116 | + "max_hourly_purchase": 20, |
| 117 | + "max_rate": 10 |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +# Run moving horizon optimization |
| 122 | +result = moving_horizon(price_data, demand_data, config) |
| 123 | + |
| 124 | +# Access optimized demand and shifts |
| 125 | +optimized = result["results"] |
| 126 | +print(optimized.head()) |
| 127 | +``` |
| 128 | + |
| 129 | +## Development |
| 130 | + |
| 131 | +Install development requirements and set up the hooks: |
| 132 | + |
| 133 | +```bash |
| 134 | +uv sync |
| 135 | +uv run pre-commit install --hook-type pre-commit --hook-type pre-push |
| 136 | +``` |
| 137 | + |
| 138 | +Before committing or pushing run: |
| 139 | + |
| 140 | +```bash |
| 141 | +uv run ruff check . |
| 142 | +uv run pytest |
| 143 | +``` |
| 144 | + |
| 145 | +## Contributing |
| 146 | + |
| 147 | +We welcome contributions! Please follow these steps: |
| 148 | + |
| 149 | +1. Fork the repository |
| 150 | +2. Create a feature branch (`git checkout -b feature/amazing-feature`) |
| 151 | +3. Make your changes |
| 152 | +4. Run the test suite (`uv run pytest`) |
| 153 | +5. Commit your changes (`git commit -m 'Add amazing feature'`) |
| 154 | +6. Push to the branch (`git push origin feature/amazing-feature`) |
| 155 | +7. Open a Pull Request |
| 156 | + |
| 157 | +Please ensure your code follows our style guidelines: |
| 158 | +- Use Ruff for code formatting and linting |
| 159 | +- Follow Google's Python style guide for docstrings |
| 160 | +- Include type annotations for all functions |
| 161 | +- Add tests for new functionality |
| 162 | + |
| 163 | +## License |
| 164 | + |
| 165 | +This project is released under the [MIT License](LICENSE). |
0 commit comments